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 39711 : fd_rust_cast_double_to_ulong( double f ) { 23 39711 : ulong u = fd_dblbits( f ); 24 : 25 : /* NaN saturates to 0. */ 26 39711 : if( FD_UNLIKELY( fd_dblbits_bexp( u )==0x7FFUL && fd_dblbits_mant( u )!=0 ) ) { 27 3 : return 0; 28 3 : } 29 : 30 : /* Negative values (including -Inf and -0.0) saturate to 0. */ 31 39708 : if( FD_UNLIKELY( fd_dblbits_sign( u )==1 ) ) { 32 6 : return 0; 33 6 : } 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 39702 : ulong bexp = fd_dblbits_bexp( u ); 41 39702 : if( FD_UNLIKELY( bexp>=1087UL ) ) { 42 9 : return ULONG_MAX; 43 9 : } 44 : 45 : /* Subnormals (bexp==0) have value < 1.0 and truncate to 0. */ 46 39693 : if( FD_UNLIKELY( bexp==0UL ) ) { 47 0 : return 0; 48 0 : } 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 39693 : ulong mant = fd_dblbits_mant( u ) | (1UL << 52); 58 39693 : int shift = 52 - (int)( bexp-1023UL ); 59 : 60 39693 : if( shift >= 64 ) { 61 0 : return 0; 62 39693 : } else if( shift>=0 ) { 63 39693 : return mant >> shift; 64 39693 : } else { 65 0 : return mant << (-shift); 66 0 : } 67 39693 : } 68 : 69 : #endif /* FD_HAS_DOUBLE */ 70 : 71 : FD_PROTOTYPES_END 72 : 73 : #endif /* HEADER_fd_src_flamenco_types_fd_cast_h */