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