7070
7171use core:: hint:: cold_path;
7272use core:: mem:: take;
73+ use core:: num:: NonZeroU64 ;
7374use std:: array:: from_fn;
7475
7576use num_bigint:: { BigInt , Sign } ;
@@ -82,13 +83,14 @@ use crate::vector::Vector;
8283
8384/// Decompose a finite `f64` into its IEEE 754 components.
8485///
85- /// Returns `None` for ±0.0, or `Some((mantissa, exponent, is_negative))` where
86- /// the value is exactly `(-1)^is_negative × mantissa × 2^exponent` and
87- /// `mantissa` is odd (trailing zeros stripped). See `REFERENCES.md` \[9-10\].
86+ /// Returns `None` for ±0.0, or `Some((mantissa, exponent, is_negative))` with a
87+ /// non-zero mantissa where the value is exactly
88+ /// `(-1)^is_negative × mantissa × 2^exponent` and `mantissa` is odd (trailing
89+ /// zeros stripped). See `REFERENCES.md` \[9-10\].
8890///
8991/// # Panics
9092/// Panics if `x` is NaN or infinite.
91- fn f64_decompose ( x : f64 ) -> Option < ( u64 , i32 , bool ) > {
93+ fn f64_decompose ( x : f64 ) -> Option < ( NonZeroU64 , i32 , bool ) > {
9294 let bits = x. to_bits ( ) ;
9395 let biased_exp = ( ( bits >> 52 ) & 0x7FF ) as i32 ;
9496 let fraction = bits & 0x000F_FFFF_FFFF_FFFF ;
@@ -114,6 +116,7 @@ fn f64_decompose(x: f64) -> Option<(u64, i32, bool)> {
114116 // Strip trailing zeros so the mantissa is odd.
115117 let tz = mantissa. trailing_zeros ( ) ;
116118 let mantissa = mantissa >> tz;
119+ let mantissa = NonZeroU64 :: new ( mantissa) ?;
117120 let exponent = raw_exp + tz. cast_signed ( ) ;
118121 let is_negative = bits >> 63 != 0 ;
119122
@@ -161,13 +164,15 @@ fn bigint_exp_to_bigrational(mut value: BigInt, mut exp: i32) -> BigRational {
161164
162165/// Decomposed finite f64 in the form `(-1)^is_negative · mantissa · 2^exponent`.
163166///
164- /// Zero entries have `mantissa == 0 `; the other fields are unused in that
165- /// case. `Default` yields such a zero component, which is what the
167+ /// Zero entries have `mantissa == None `; the other fields are unused in that
168+ /// case. `Default` yields such a zero component, which is what the
166169/// per-entry initialiser in `decompose_matrix` / `decompose_vec` produces
167- /// for ±0.0 cells.
170+ /// for ±0.0 cells. Non-zero entries carry a [`NonZeroU64`] mantissa, so the
171+ /// exact-arithmetic paths cannot accidentally store a raw zero sentinel after
172+ /// decomposition.
168173#[ derive( Clone , Copy , Default ) ]
169174struct Component {
170- mantissa : u64 ,
175+ mantissa : Option < NonZeroU64 > ,
171176 exponent : i32 ,
172177 is_negative : bool ,
173178}
@@ -191,7 +196,7 @@ fn decompose_matrix<const D: usize>(m: &Matrix<D>) -> Result<([[Component; D]; D
191196 }
192197 if let Some ( ( mantissa, exponent, is_negative) ) = f64_decompose ( entry) {
193198 components[ r] [ c] = Component {
194- mantissa,
199+ mantissa : Some ( mantissa ) ,
195200 exponent,
196201 is_negative,
197202 } ;
@@ -220,7 +225,7 @@ fn decompose_vec<const D: usize>(v: &Vector<D>) -> Result<([Component; D], i32),
220225 }
221226 if let Some ( ( mantissa, exponent, is_negative) ) = f64_decompose ( entry) {
222227 components[ i] = Component {
223- mantissa,
228+ mantissa : Some ( mantissa ) ,
224229 exponent,
225230 is_negative,
226231 } ;
@@ -232,15 +237,17 @@ fn decompose_vec<const D: usize>(v: &Vector<D>) -> Result<([Component; D], i32),
232237
233238/// Convert a single decomposed component to its scaled `BigInt`
234239/// representation: `(±mantissa) << (exp − e_min)`. Zero components map
235- /// to `BigInt::from(0)`.
240+ /// to `BigInt::from(0)` through the `None` case; non-zero components reuse
241+ /// their carried [`NonZeroU64`] proof without revalidating the mantissa.
236242#[ inline]
237243fn component_to_bigint ( c : Component , e_min : i32 ) -> BigInt {
238- if c. mantissa == 0 {
239- BigInt :: from ( 0 )
240- } else {
241- let v = BigInt :: from ( c. mantissa ) << ( c. exponent - e_min) . cast_unsigned ( ) ;
242- if c. is_negative { -v } else { v }
243- }
244+ c. mantissa . map_or_else (
245+ || BigInt :: from ( 0 ) ,
246+ |mantissa| {
247+ let v = BigInt :: from ( mantissa. get ( ) ) << ( c. exponent - e_min) . cast_unsigned ( ) ;
248+ if c. is_negative { -v } else { v }
249+ } ,
250+ )
244251}
245252
246253/// Build a `D×D` integer matrix from a component table, scaled to the
@@ -758,9 +765,9 @@ mod tests {
758765 } ;
759766
760767 let numer = if is_negative {
761- -BigInt :: from ( mantissa)
768+ -BigInt :: from ( mantissa. get ( ) )
762769 } else {
763- BigInt :: from ( mantissa)
770+ BigInt :: from ( mantissa. get ( ) )
764771 } ;
765772
766773 if exponent >= 0 {
@@ -1158,7 +1165,7 @@ mod tests {
11581165 #[ test]
11591166 fn f64_decompose_one ( ) {
11601167 let ( mant, exp, neg) = f64_decompose ( 1.0 ) . unwrap ( ) ;
1161- assert_eq ! ( mant, 1 ) ;
1168+ assert_eq ! ( mant. get ( ) , 1 ) ;
11621169 assert_eq ! ( exp, 0 ) ;
11631170 assert ! ( !neg) ;
11641171 }
@@ -1167,7 +1174,7 @@ mod tests {
11671174 fn f64_decompose_negative ( ) {
11681175 let ( mant, exp, neg) = f64_decompose ( -3.5 ) . unwrap ( ) ;
11691176 // -3.5 = -7 × 2^(-1), mantissa is 7 (odd after stripping)
1170- assert_eq ! ( mant, 7 ) ;
1177+ assert_eq ! ( mant. get ( ) , 7 ) ;
11711178 assert_eq ! ( exp, -1 ) ;
11721179 assert ! ( neg) ;
11731180 }
@@ -1177,15 +1184,15 @@ mod tests {
11771184 let tiny = 5e-324_f64 ;
11781185 assert ! ( tiny. is_subnormal( ) ) ;
11791186 let ( mant, exp, neg) = f64_decompose ( tiny) . unwrap ( ) ;
1180- assert_eq ! ( mant, 1 ) ;
1187+ assert_eq ! ( mant. get ( ) , 1 ) ;
11811188 assert_eq ! ( exp, -1074 ) ;
11821189 assert ! ( !neg) ;
11831190 }
11841191
11851192 #[ test]
11861193 fn f64_decompose_power_of_two ( ) {
11871194 let ( mant, exp, neg) = f64_decompose ( 1024.0 ) . unwrap ( ) ;
1188- assert_eq ! ( mant, 1 ) ;
1195+ assert_eq ! ( mant. get ( ) , 1 ) ;
11891196 assert_eq ! ( exp, 10 ) ; // 1024 = 2^10
11901197 assert ! ( !neg) ;
11911198 }
@@ -1196,6 +1203,28 @@ mod tests {
11961203 f64_decompose ( f64:: NAN ) ;
11971204 }
11981205
1206+ #[ test]
1207+ fn component_to_bigint_distinguishes_zero_from_nonzero_mantissa ( ) {
1208+ assert_eq ! (
1209+ component_to_bigint( Component :: default ( ) , -10 ) ,
1210+ BigInt :: from( 0 )
1211+ ) ;
1212+
1213+ let positive = Component {
1214+ mantissa : NonZeroU64 :: new ( 3 ) ,
1215+ exponent : 4 ,
1216+ is_negative : false ,
1217+ } ;
1218+ assert_eq ! ( component_to_bigint( positive, 1 ) , BigInt :: from( 24 ) ) ;
1219+
1220+ let negative = Component {
1221+ mantissa : NonZeroU64 :: new ( 5 ) ,
1222+ exponent : 3 ,
1223+ is_negative : true ,
1224+ } ;
1225+ assert_eq ! ( component_to_bigint( negative, 1 ) , BigInt :: from( -20 ) ) ;
1226+ }
1227+
11991228 // -----------------------------------------------------------------------
12001229 // bareiss_det_int tests
12011230 // -----------------------------------------------------------------------
0 commit comments