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 */