LCOV - code coverage report
Current view: top level - flamenco/types - fd_cast.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 18 55 32.7 %
Date: 2026-10-22 18:10:43 Functions: 3 126 2.4 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_types_fd_cast_h
       2             : #define HEADER_fd_src_flamenco_types_fd_cast_h
       3             : 
       4             : #include "../../util/bits/fd_float.h"
       5             : 
       6             : /* From https://doc.rust-lang.org/rust-by-example/types/cast.html
       7             : 
       8             :    Since Rust 1.45, the `as` keyword performs a *saturating cast*
       9             :    when casting from float to int. If the floating point value exceeds
      10             :    the upper bound or is less than the lower bound, the returned value
      11             :    will be equal to the bound crossed. */
      12             : 
      13             : FD_PROTOTYPES_BEGIN
      14             : 
      15             : /* Cast a double to unsigned long with identical behaviour to Rust's
      16             :    saturating "as" case.
      17             :    Saturate to 0 if the value is negative or NaN.
      18             :    Saturate to ULONG_MAX if the value is greater than ULONG_MAX. */
      19             : FD_FN_CONST static inline ulong
      20       15026 : fd_rust_cast_double_to_ulong( double f ) {
      21       15026 :   ulong u = fd_dblbits( f );
      22             : 
      23             :   /* NaN saturates to 0. */
      24       15026 :   if( FD_UNLIKELY( fd_dblbits_bexp( u )==0x7FFUL && fd_dblbits_mant( u )!=0 ) ) {
      25           0 :     return 0;
      26           0 :   }
      27             : 
      28             :   /* Negative values (including -Inf and -0.0) saturate to 0. */
      29       15026 :   if( FD_UNLIKELY( fd_dblbits_sign( u )==1 ) ) {
      30           0 :     return 0;
      31           0 :   }
      32             : 
      33             :   /* +Inf or values >= 2^64 saturate to ULONG_MAX.
      34             :       A positive double has value 1.mant * 2^(bexp-1023).
      35             :       When bexp >= 1087 (exponent >= 64), the value is >= 2^64
      36             :       and cannot fit in a ulong. bexp=0x7FF (+Inf) also caught here
      37             :       but NaN was already handled above. */
      38       15026 :   ulong bexp = fd_dblbits_bexp( u );
      39       15026 :   if( FD_UNLIKELY( bexp>=1087UL ) ) {
      40           0 :     return ULONG_MAX;
      41           0 :   }
      42             : 
      43             :   /* Subnormals (bexp==0) have value < 1.0 and truncate to 0. */
      44       15026 :   if( FD_UNLIKELY( bexp==0UL ) ) {
      45          13 :     return 0;
      46          13 :   }
      47             : 
      48             :   /* Normal value in [0, 2^64), is safe to convert.
      49             : 
      50             :      value = (1 << 52 | mantissa) >> (52 - (bexp - 1023))
      51             :      when bexp-1023 > 52, shift left instead
      52             :      bexp is within [1, 1086], so exponent = bexp-1023 is within [-1022, 63]
      53             :      shift = 52 - exponent is within [-11, 1074]
      54             :      For shift >= 64, result is 0 (very small positive numbers). */
      55       15013 :   ulong mant = fd_dblbits_mant( u ) | (1UL << 52);
      56       15013 :   int  shift = 52 - (int)( bexp-1023UL );
      57             : 
      58       15013 :   if( shift >= 64 ) {
      59           0 :     return 0;
      60       15013 :   } else if( shift>=0 ) {
      61       14991 :     return mant >> shift;
      62       14991 :   } else {
      63          22 :     return mant << (-shift);
      64          22 :   }
      65       15013 : }
      66             : 
      67             : /* The remaining integer widths, same Rust `as` semantics: nan becomes 0
      68             :    and out of range values saturate at the bound crossed.  A plain C cast
      69             :    is undefined behaviour for those inputs.
      70             : 
      71             :    The bounds are compared as doubles.  2^63, 2^32 and 2^31 are exactly
      72             :    representable, so `>=` against them rejects precisely what does not
      73             :    fit. */
      74             : 
      75             : FD_FN_CONST static inline long
      76           0 : fd_rust_cast_double_to_long( double f ) {
      77           0 :   if( FD_UNLIKELY( f!=f                     ) ) return 0L;        /* nan */
      78           0 :   if( FD_UNLIKELY( f>= 9223372036854775808. ) ) return LONG_MAX;  /*  2^63 */
      79           0 :   if( FD_UNLIKELY( f<=-9223372036854775808. ) ) return LONG_MIN;  /* -2^63 */
      80           0 :   return (long)f;
      81           0 : }
      82             : 
      83             : FD_FN_CONST static inline uint
      84           0 : fd_rust_cast_double_to_uint( double f ) {
      85           0 :   if( FD_UNLIKELY( f!=f            ) ) return 0U;        /* nan */
      86           0 :   if( FD_UNLIKELY( f>=4294967296.  ) ) return UINT_MAX;  /* 2^32 */
      87           0 :   if( FD_UNLIKELY( !(f>0.)         ) ) return 0U;        /* <=0, and nan already gone */
      88           0 :   return (uint)f;
      89           0 : }
      90             : 
      91             : FD_FN_CONST static inline int
      92           0 : fd_rust_cast_double_to_int( double f ) {
      93           0 :   if( FD_UNLIKELY( f!=f            ) ) return 0;        /* nan */
      94           0 :   if( FD_UNLIKELY( f>= 2147483648. ) ) return INT_MAX;  /*  2^31 */
      95           0 :   if( FD_UNLIKELY( f<=-2147483648. ) ) return INT_MIN;  /* -2^31 */
      96           0 :   return (int)f;
      97           0 : }
      98             : 
      99             : /* Single precision sources, same semantics. */
     100             : 
     101             : FD_FN_CONST static inline ulong
     102           0 : fd_rust_cast_float_to_ulong( float f ) {
     103           0 :   if( FD_UNLIKELY( f!=f                      ) ) return 0UL;       /* nan */
     104           0 :   if( FD_UNLIKELY( f>=18446744073709551616.f ) ) return ULONG_MAX; /* 2^64 */
     105           0 :   if( FD_UNLIKELY( !(f>0.f)                  ) ) return 0UL;       /* <=0 */
     106           0 :   return (ulong)f;
     107           0 : }
     108             : 
     109             : FD_FN_CONST static inline uint
     110           0 : fd_rust_cast_float_to_uint( float f ) {
     111           0 :   if( FD_UNLIKELY( f!=f              ) ) return 0U;       /* nan */
     112           0 :   if( FD_UNLIKELY( f>=4294967296.f   ) ) return UINT_MAX; /* 2^32 */
     113           0 :   if( FD_UNLIKELY( !(f>0.f)          ) ) return 0U;       /* <=0 */
     114           0 :   return (uint)f;
     115           0 : }
     116             : 
     117             : FD_PROTOTYPES_END
     118             : 
     119             : #endif /* HEADER_fd_src_flamenco_types_fd_cast_h */

Generated by: LCOV version 1.14