@@ -4859,67 +4859,179 @@ const FP_INT_TONEARESTFROMZERO: c_int = 3;
48594859#[ allow( dead_code) ]
48604860const FP_INT_TONEAREST : c_int = 4 ;
48614861
4862- fn fromfp_impl ( x : f64 , rnd : c_int , width : u32 ) -> f64 {
4863- if x. is_nan ( ) || x. is_infinite ( ) {
4864- return f64:: NAN ;
4865- }
4866- let r = match rnd {
4862+ /// Raise FE_INEXACT via a safe force_eval (1/3 is inexact). Used by the
4863+ /// fromfpx/ufromfpx variants when the kept (in-range) result differs from x.
4864+ #[ inline]
4865+ fn raise_inexact_f64 ( ) {
4866+ let _ = core:: hint:: black_box ( core:: hint:: black_box ( 1.0_f64 ) / core:: hint:: black_box ( 3.0_f64 ) ) ;
4867+ }
4868+
4869+ /// 2^n for n in 0..=64, exact and FE-flag-free (bit construction). `2f64.powi(n)`
4870+ /// with a runtime n lowers to a libm pow call that raises FE_INEXACT even though
4871+ /// 2^n is exact, which would pollute fromfp's flag contract.
4872+ #[ inline]
4873+ fn pow2_exact ( n : u32 ) -> f64 {
4874+ f64:: from_bits ( ( 1023u64 + n as u64 ) << 52 )
4875+ }
4876+
4877+ /// Round in the requested FP_INT direction WITHOUT raising FP exceptions.
4878+ /// `fromfp` must raise only FE_INVALID (the *x variants add FE_INEXACT
4879+ /// explicitly), so the rounding itself must be flag-free. ceil/floor/trunc
4880+ /// lower to suppress-precision roundsd; round-half-away/even are built from
4881+ /// trunc + exact integer arithmetic (x - trunc(x) is exact; adding ±1 to an
4882+ /// integer-valued f64 is exact). `f64::round` is NOT used: it raises INEXACT.
4883+ #[ inline]
4884+ fn fromfp_round ( x : f64 , rnd : c_int ) -> f64 {
4885+ match rnd {
48674886 FP_INT_UPWARD => x. ceil ( ) ,
48684887 FP_INT_DOWNWARD => x. floor ( ) ,
48694888 FP_INT_TOWARDZERO => x. trunc ( ) ,
4870- FP_INT_TONEARESTFROMZERO => x. round ( ) ,
4889+ FP_INT_TONEARESTFROMZERO => {
4890+ let t = x. trunc ( ) ;
4891+ if ( x - t) . abs ( ) >= 0.5 { t + x. signum ( ) } else { t }
4892+ }
48714893 _ => {
4872- let r = x. round ( ) ;
4873- if ( x - r) . abs ( ) == 0.5 {
4874- if ( r as i64 ) % 2 != 0 {
4875- r - r. signum ( )
4876- } else {
4877- r
4878- }
4894+ // FP_INT_TONEAREST: round half to even.
4895+ let t = x. trunc ( ) ;
4896+ let af = ( x - t) . abs ( ) ;
4897+ if af < 0.5 {
4898+ t
4899+ } else if af > 0.5 {
4900+ t + x. signum ( )
4901+ } else if ( t as i64 ) % 2 == 0 {
4902+ t // tie: t already even
48794903 } else {
4880- r
4904+ t + x . signum ( )
48814905 }
48824906 }
4883- } ;
4884- if width == 0 || width > 64 {
4885- return f64:: NAN ;
48864907 }
4887- let max = if width >= 64 {
4888- i64:: MAX
4908+ }
4909+
4910+ /// Shared core for fromfp/fromfpx. Matches glibc: round x in the requested
4911+ /// direction to a signed integer of `width` bits; NaN/±inf and out-of-range
4912+ /// results raise FE_INVALID and return glibc's clamp (max for NaN/+inf/overflow,
4913+ /// min for -inf/underflow). With `inexact`, also raise FE_INEXACT when the kept
4914+ /// result differs from x (the fromfpx variant).
4915+ const FE_INVALID_BIT : c_int = 0x01 ;
4916+ const FE_INEXACT_BIT : c_int = 0x20 ;
4917+
4918+ unsafe extern "C" {
4919+ fn feraiseexcept ( excepts : c_int ) -> c_int ;
4920+ fn feclearexcept ( excepts : c_int ) -> c_int ;
4921+ fn fetestexcept ( excepts : c_int ) -> c_int ;
4922+ }
4923+
4924+ /// Set the FP exception state for the fromfp family to EXACTLY the caller's
4925+ /// pre-existing flags plus `want` ( FE_INVALID /FE_INEXACT ) . The decision logic
4926+ /// itself performs FP arithmetic (rounding, 2^w construction) that can raise
4927+ /// spurious flags; computing the intended flags as pure booleans and then
4928+ /// stamping them here makes the contract exact regardless.
4929+ #[ inline]
4930+ fn fromfp_set_flags ( entry : c_int , want : c_int ) {
4931+ unsafe {
4932+ feclearexcept ( FE_INVALID_BIT | FE_INEXACT_BIT ) ;
4933+ let f = ( entry & ( FE_INVALID_BIT | FE_INEXACT_BIT ) ) | want;
4934+ if f != 0 {
4935+ feraiseexcept ( f) ;
4936+ }
4937+ }
4938+ }
4939+
4940+ fn fromfp_core ( x : f64 , rnd : c_int , width : u32 , inexact : bool ) -> f64 {
4941+ let entry = unsafe { fetestexcept ( FE_INVALID_BIT | FE_INEXACT_BIT ) } ;
4942+ let w = width. min ( 64 ) ;
4943+ if w == 0 {
4944+ fromfp_set_flags ( entry, FE_INVALID_BIT ) ;
4945+ return 0.0 ;
4946+ }
4947+ let thresh_hi = pow2_exact ( w - 1 ) ; // 2^(w-1): first out-of-range high value
4948+ // max = 2^(w-1)-1, exact iff w <= 54; for wider w it rounds to 2^(w-1)
4949+ // (= thresh_hi), exactly what glibc returns — and that rounding means a high
4950+ // overflow raises FE_INEXACT alongside FE_INVALID. The min is exact.
4951+ let hi_inexact = if w > 54 { FE_INEXACT_BIT } else { 0 } ;
4952+ let clamp_hi = if w <= 54 { thresh_hi - 1.0 } else { thresh_hi } ;
4953+ let clamp_lo = -thresh_hi; // min = -2^(w-1)
4954+ let ( value, want) = if x. is_nan ( ) {
4955+ ( clamp_hi, FE_INVALID_BIT | hi_inexact)
4956+ } else if x. is_infinite ( ) {
4957+ if x > 0.0 {
4958+ ( clamp_hi, FE_INVALID_BIT | hi_inexact)
4959+ } else {
4960+ ( clamp_lo, FE_INVALID_BIT )
4961+ }
48894962 } else {
4890- ( 1i64 << ( width - 1 ) ) - 1
4963+ let r = fromfp_round ( x, rnd) ;
4964+ if r >= thresh_hi {
4965+ ( clamp_hi, FE_INVALID_BIT | hi_inexact)
4966+ } else if r < clamp_lo {
4967+ ( clamp_lo, FE_INVALID_BIT )
4968+ } else {
4969+ let inx = if inexact && r != x { FE_INEXACT_BIT } else { 0 } ;
4970+ ( if r == 0.0 { 0.0 } else { r } , inx) // glibc normalises a zero to +0
4971+ }
48914972 } ;
4892- let min = if width >= 64 {
4893- i64:: MIN
4973+ fromfp_set_flags ( entry, want) ;
4974+ value
4975+ }
4976+
4977+ /// Shared core for ufromfp/ufromfpx. Unsigned range [0, 2^width-1]; negatives
4978+ /// that round into range are valid (e.g. ufromfp(-0.4, UPWARD) = 0).
4979+ fn ufromfp_core ( x : f64 , rnd : c_int , width : u32 , inexact : bool ) -> f64 {
4980+ let entry = unsafe { fetestexcept ( FE_INVALID_BIT | FE_INEXACT_BIT ) } ;
4981+ let w = width. min ( 64 ) ;
4982+ if w == 0 {
4983+ fromfp_set_flags ( entry, FE_INVALID_BIT ) ;
4984+ return 0.0 ;
4985+ }
4986+ let thresh_hi = pow2_exact ( w) ; // 2^w
4987+ let hi_inexact = if w > 53 { FE_INEXACT_BIT } else { 0 } ;
4988+ let clamp_hi = if w <= 53 { thresh_hi - 1.0 } else { thresh_hi } ;
4989+ let ( value, want) = if x. is_nan ( ) {
4990+ ( clamp_hi, FE_INVALID_BIT | hi_inexact)
4991+ } else if x. is_infinite ( ) {
4992+ if x > 0.0 {
4993+ ( clamp_hi, FE_INVALID_BIT | hi_inexact)
4994+ } else {
4995+ ( 0.0 , FE_INVALID_BIT )
4996+ }
48944997 } else {
4895- -( 1i64 << ( width - 1 ) )
4998+ let r = fromfp_round ( x, rnd) ;
4999+ if r >= thresh_hi {
5000+ ( clamp_hi, FE_INVALID_BIT | hi_inexact)
5001+ } else if r < 0.0 {
5002+ ( 0.0 , FE_INVALID_BIT )
5003+ } else {
5004+ let inx = if inexact && r != x { FE_INEXACT_BIT } else { 0 } ;
5005+ ( if r == 0.0 { 0.0 } else { r } , inx) // normalise -0 -> +0
5006+ }
48965007 } ;
4897- let ri = r as i64 ;
4898- if ri < min || ri > max { f64:: NAN } else { r }
5008+ fromfp_set_flags ( entry, want) ;
5009+ value
5010+ }
5011+
5012+ fn fromfp_impl ( x : f64 , rnd : c_int , width : u32 ) -> f64 {
5013+ fromfp_core ( x, rnd, width, false )
5014+ }
5015+ fn fromfpx_impl ( x : f64 , rnd : c_int , width : u32 ) -> f64 {
5016+ fromfp_core ( x, rnd, width, true )
48995017}
49005018fn fromfpf_impl ( x : f32 , rnd : c_int , width : u32 ) -> f32 {
4901- fromfp_impl ( x as f64 , rnd, width) as f32
5019+ fromfp_core ( x as f64 , rnd, width, false ) as f32
5020+ }
5021+ fn fromfpxf_impl ( x : f32 , rnd : c_int , width : u32 ) -> f32 {
5022+ fromfp_core ( x as f64 , rnd, width, true ) as f32
49025023}
49035024fn ufromfp_impl ( x : f64 , rnd : c_int , width : u32 ) -> f64 {
4904- if x. is_nan ( ) || x. is_infinite ( ) || x < 0.0 {
4905- return f64:: NAN ;
4906- }
4907- let r = fromfp_impl ( x, rnd, 64 ) ;
4908- if r. is_nan ( ) || r < 0.0 {
4909- return f64:: NAN ;
4910- }
4911- if width == 0 || width > 64 {
4912- return f64:: NAN ;
4913- }
4914- let max = if width >= 64 {
4915- u64:: MAX
4916- } else {
4917- ( 1u64 << width) - 1
4918- } ;
4919- if ( r as u64 ) > max { f64:: NAN } else { r }
5025+ ufromfp_core ( x, rnd, width, false )
5026+ }
5027+ fn ufromfpx_impl ( x : f64 , rnd : c_int , width : u32 ) -> f64 {
5028+ ufromfp_core ( x, rnd, width, true )
49205029}
49215030fn ufromfpf_impl ( x : f32 , rnd : c_int , width : u32 ) -> f32 {
4922- ufromfp_impl ( x as f64 , rnd, width) as f32
5031+ ufromfp_core ( x as f64 , rnd, width, false ) as f32
5032+ }
5033+ fn ufromfpxf_impl ( x : f32 , rnd : c_int , width : u32 ) -> f32 {
5034+ ufromfp_core ( x as f64 , rnd, width, true ) as f32
49235035}
49245036
49255037#[ cfg_attr( not( debug_assertions) , unsafe ( no_mangle) ) ]
@@ -4988,11 +5100,11 @@ pub unsafe extern "C" fn ufromfpf128(x: f64, rnd: c_int, width: u32) -> f64 {
49885100}
49895101#[ cfg_attr( not( debug_assertions) , unsafe ( no_mangle) ) ]
49905102pub unsafe extern "C" fn fromfpx ( x : f64 , rnd : c_int , width : u32 ) -> f64 {
4991- fromfp_impl ( x, rnd, width)
5103+ fromfpx_impl ( x, rnd, width)
49925104}
49935105#[ cfg_attr( not( debug_assertions) , unsafe ( no_mangle) ) ]
49945106pub unsafe extern "C" fn fromfpxf ( x : f32 , rnd : c_int , width : u32 ) -> f32 {
4995- fromfpf_impl ( x, rnd, width)
5107+ fromfpxf_impl ( x, rnd, width)
49965108}
49975109#[ cfg_attr( not( debug_assertions) , unsafe ( no_mangle) ) ]
49985110pub unsafe extern "C" fn fromfpxl ( x : f64 , rnd : c_int , width : u32 ) -> f64 {
@@ -5020,11 +5132,11 @@ pub unsafe extern "C" fn fromfpxf128(x: f64, rnd: c_int, width: u32) -> f64 {
50205132}
50215133#[ cfg_attr( not( debug_assertions) , unsafe ( no_mangle) ) ]
50225134pub unsafe extern "C" fn ufromfpx ( x : f64 , rnd : c_int , width : u32 ) -> f64 {
5023- ufromfp_impl ( x, rnd, width)
5135+ ufromfpx_impl ( x, rnd, width)
50245136}
50255137#[ cfg_attr( not( debug_assertions) , unsafe ( no_mangle) ) ]
50265138pub unsafe extern "C" fn ufromfpxf ( x : f32 , rnd : c_int , width : u32 ) -> f32 {
5027- ufromfpf_impl ( x, rnd, width)
5139+ ufromfpxf_impl ( x, rnd, width)
50285140}
50295141#[ cfg_attr( not( debug_assertions) , unsafe ( no_mangle) ) ]
50305142pub unsafe extern "C" fn ufromfpxl ( x : f64 , rnd : c_int , width : u32 ) -> f64 {
0 commit comments