LCOV - code coverage report
Current view: top level - flamenco/types - fd_cast.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 55 55 100.0 %
Date: 2026-09-09 04:28:17 Functions: 9 78 11.5 %

          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             : #if FD_HAS_DOUBLE
      16             : 
      17             : /* Cast a double to unsigned long with identical behaviour to Rust's
      18             :    saturating "as" case.
      19             :    Saturate to 0 if the value is negative or NaN.
      20             :    Saturate to ULONG_MAX if the value is greater than ULONG_MAX. */
      21             : FD_FN_CONST static inline ulong
      22     6000570 : fd_rust_cast_double_to_ulong( double f ) {
      23     6000570 :   ulong u = fd_dblbits( f );
      24             : 
      25             :   /* NaN saturates to 0. */
      26     6000570 :   if( FD_UNLIKELY( fd_dblbits_bexp( u )==0x7FFUL && fd_dblbits_mant( u )!=0 ) ) {
      27          21 :     return 0;
      28          21 :   }
      29             : 
      30             :   /* Negative values (including -Inf and -0.0) saturate to 0. */
      31     6000549 :   if( FD_UNLIKELY( fd_dblbits_sign( u )==1 ) ) {
      32         195 :     return 0;
      33         195 :   }
      34             : 
      35             :   /* +Inf or values >= 2^64 saturate to ULONG_MAX.
      36             :       A positive double has value 1.mant * 2^(bexp-1023).
      37             :       When bexp >= 1087 (exponent >= 64), the value is >= 2^64
      38             :       and cannot fit in a ulong. bexp=0x7FF (+Inf) also caught here
      39             :       but NaN was already handled above. */
      40     6000354 :   ulong bexp = fd_dblbits_bexp( u );
      41     6000354 :   if( FD_UNLIKELY( bexp>=1087UL ) ) {
      42      315756 :     return ULONG_MAX;
      43      315756 :   }
      44             : 
      45             :   /* Subnormals (bexp==0) have value < 1.0 and truncate to 0. */
      46     5684598 :   if( FD_UNLIKELY( bexp==0UL ) ) {
      47           9 :     return 0;
      48           9 :   }
      49             : 
      50             :   /* Normal value in [0, 2^64), is safe to convert.
      51             : 
      52             :      value = (1 << 52 | mantissa) >> (52 - (bexp - 1023))
      53             :      when bexp-1023 > 52, shift left instead
      54             :      bexp is within [1, 1086], so exponent = bexp-1023 is within [-1022, 63]
      55             :      shift = 52 - exponent is within [-11, 1074]
      56             :      For shift >= 64, result is 0 (very small positive numbers). */
      57     5684589 :   ulong mant = fd_dblbits_mant( u ) | (1UL << 52);
      58     5684589 :   int  shift = 52 - (int)( bexp-1023UL );
      59             : 
      60     5684589 :   if( shift >= 64 ) {
      61         105 :     return 0;
      62     5684484 :   } else if( shift>=0 ) {
      63       61242 :     return mant >> shift;
      64     5623242 :   } else {
      65     5623242 :     return mant << (-shift);
      66     5623242 :   }
      67     5684589 : }
      68             : 
      69             : /* The remaining integer widths, same Rust `as` semantics: nan becomes 0
      70             :    and out of range values saturate at the bound crossed.  A plain C cast
      71             :    is undefined behaviour for those inputs.
      72             : 
      73             :    The bounds are compared as doubles.  2^63, 2^32 and 2^31 are exactly
      74             :    representable, so `>=` against them rejects precisely what does not
      75             :    fit. */
      76             : 
      77             : FD_FN_CONST static inline long
      78         489 : fd_rust_cast_double_to_long( double f ) {
      79         489 :   if( FD_UNLIKELY( f!=f                     ) ) return 0L;        /* nan */
      80         471 :   if( FD_UNLIKELY( f>= 9223372036854775808. ) ) return LONG_MAX;  /*  2^63 */
      81         348 :   if( FD_UNLIKELY( f<=-9223372036854775808. ) ) return LONG_MIN;  /* -2^63 */
      82         264 :   return (long)f;
      83         348 : }
      84             : 
      85             : FD_FN_CONST static inline uint
      86         489 : fd_rust_cast_double_to_uint( double f ) {
      87         489 :   if( FD_UNLIKELY( f!=f            ) ) return 0U;        /* nan */
      88         471 :   if( FD_UNLIKELY( f>=4294967296.  ) ) return UINT_MAX;  /* 2^32 */
      89         333 :   if( FD_UNLIKELY( !(f>0.)         ) ) return 0U;        /* <=0, and nan already gone */
      90         141 :   return (uint)f;
      91         333 : }
      92             : 
      93             : FD_FN_CONST static inline int
      94         489 : fd_rust_cast_double_to_int( double f ) {
      95         489 :   if( FD_UNLIKELY( f!=f            ) ) return 0;        /* nan */
      96         471 :   if( FD_UNLIKELY( f>= 2147483648. ) ) return INT_MAX;  /*  2^31 */
      97         333 :   if( FD_UNLIKELY( f<=-2147483648. ) ) return INT_MIN;  /* -2^31 */
      98         249 :   return (int)f;
      99         333 : }
     100             : 
     101             : #endif /* FD_HAS_DOUBLE */
     102             : 
     103             : /* Single precision sources, same semantics. */
     104             : 
     105             : FD_FN_CONST static inline ulong
     106         489 : fd_rust_cast_float_to_ulong( float f ) {
     107         489 :   if( FD_UNLIKELY( f!=f                      ) ) return 0UL;       /* nan */
     108         471 :   if( FD_UNLIKELY( f>=18446744073709551616.f ) ) return ULONG_MAX; /* 2^64 */
     109         351 :   if( FD_UNLIKELY( !(f>0.f)                  ) ) return 0UL;       /* <=0 */
     110          63 :   return (ulong)f;
     111         351 : }
     112             : 
     113             : FD_FN_CONST static inline uint
     114         489 : fd_rust_cast_float_to_uint( float f ) {
     115         489 :   if( FD_UNLIKELY( f!=f              ) ) return 0U;       /* nan */
     116         471 :   if( FD_UNLIKELY( f>=4294967296.f   ) ) return UINT_MAX; /* 2^32 */
     117         333 :   if( FD_UNLIKELY( !(f>0.f)          ) ) return 0U;       /* <=0 */
     118          45 :   return (uint)f;
     119         333 : }
     120             : 
     121             : FD_PROTOTYPES_END
     122             : 
     123             : #endif /* HEADER_fd_src_flamenco_types_fd_cast_h */

Generated by: LCOV version 1.14