1 //! Defines rounding schemes for floating-point numbers.
2 
3 use crate::util::*;
4 use super::float::ExtendedFloat;
5 use super::mantissa::Mantissa;
6 use super::shift::*;
7 
8 // GENERIC
9 // -------
10 
11 // NEAREST ROUNDING
12 
13 // Shift right N-bytes and round to the nearest.
14 //
15 // Return if we are above halfway and if we are halfway.
16 perftools_inline!{
17 pub(crate) fn round_nearest<M>(fp: &mut ExtendedFloat<M>, shift: i32)
18     -> (bool, bool)
19     where M: Mantissa
20 {
21     // Extract the truncated bits using mask.
22     // Calculate if the value of the truncated bits are either above
23     // the mid-way point, or equal to it.
24     //
25     // For example, for 4 truncated bytes, the mask would be b1111
26     // and the midway point would be b1000.
27     let mask: M = lower_n_mask(as_cast(shift));
28     let halfway: M = lower_n_halfway(as_cast(shift));
29 
30     let truncated_bits = fp.mant & mask;
31     let is_above = truncated_bits > halfway;
32     let is_halfway = truncated_bits == halfway;
33 
34     // Bit shift so the leading bit is in the hidden bit.
35     overflowing_shr(fp, shift);
36 
37     (is_above, is_halfway)
38 }}
39 
40 // Tie rounded floating point to event.
41 perftools_inline!{
42 pub(crate) fn tie_even<M>(fp: &mut ExtendedFloat<M>, is_above: bool, is_halfway: bool)
43     where M: Mantissa
44 {
45     // Extract the last bit after shifting (and determine if it is odd).
46     let is_odd = fp.mant & M::ONE == M::ONE;
47 
48     // Calculate if we need to roundup.
49     // We need to roundup if we are above halfway, or if we are odd
50     // and at half-way (need to tie-to-even).
51     if is_above || (is_odd && is_halfway) {
52         fp.mant += M::ONE;
53     }
54 }}
55 
56 // Shift right N-bytes and round nearest, tie-to-even.
57 //
58 // Floating-point arithmetic uses round to nearest, ties to even,
59 // which rounds to the nearest value, if the value is halfway in between,
60 // round to an even value.
61 perftools_inline!{
62 pub(crate) fn round_nearest_tie_even<M>(fp: &mut ExtendedFloat<M>, shift: i32)
63     where M: Mantissa
64 {
65     let (is_above, is_halfway) = round_nearest(fp, shift);
66     tie_even(fp, is_above, is_halfway);
67 }}
68 
69 // Tie rounded floating point away from zero.
70 perftools_inline!{
71 pub(crate) fn tie_away_zero<M>(fp: &mut ExtendedFloat<M>, is_above: bool, is_halfway: bool)
72     where M: Mantissa
73 {
74     // Calculate if we need to roundup.
75     // We need to roundup if we are halfway or above halfway,
76     // since the value is always positive and we need to round away
77     // from zero.
78     if is_above || is_halfway {
79         fp.mant += M::ONE;
80     }
81 }}
82 
83 // Shift right N-bytes and round nearest, tie-away-zero.
84 //
85 // Floating-point arithmetic defines round to nearest, ties away from zero,
86 // which rounds to the nearest value, if the value is halfway in between,
87 // ties away from zero.
88 perftools_inline!{
89 pub(crate) fn round_nearest_tie_away_zero<M>(fp: &mut ExtendedFloat<M>, shift: i32)
90     where M: Mantissa
91 {
92     let (is_above, is_halfway) = round_nearest(fp, shift);
93     tie_away_zero(fp, is_above, is_halfway);
94 }}
95 
96 // DIRECTED ROUNDING
97 
98 // Shift right N-bytes and round towards a direction.
99 //
100 // Return if we have any truncated bytes.
101 perftools_inline!{
102 pub(crate) fn round_toward<M>(fp: &mut ExtendedFloat<M>, shift: i32)
103     -> bool
104     where M: Mantissa
105 {
106     let mask: M = lower_n_mask(as_cast(shift));
107     let truncated_bits = fp.mant & mask;
108 
109     // Bit shift so the leading bit is in the hidden bit.
110     overflowing_shr(fp, shift);
111 
112     truncated_bits != M::ZERO
113 }}
114 
115 // Round up.
116 perftools_inline!{
117 pub(crate) fn upward<M>(fp: &mut ExtendedFloat<M>, is_truncated: bool)
118     where M: Mantissa
119 {
120     if is_truncated {
121         fp.mant += M::ONE;
122     }
123 }}
124 
125 // Shift right N-bytes and round toward infinity.
126 //
127 // Floating-point arithmetic defines round toward infinity, which rounds
128 // towards positive infinity.
129 perftools_inline!{
130 pub(crate) fn round_upward<M>(fp: &mut ExtendedFloat<M>, shift: i32)
131     where M: Mantissa
132 {
133     // If the truncated bits are non-zero, that is, any rounding error occurred,
134     // round-up.
135     let is_truncated = round_toward(fp, shift);
136     upward(fp, is_truncated);
137 }}
138 
139 // Round down.
140 perftools_inline!{
141 pub(crate) fn downard<M>(_: &mut ExtendedFloat<M>, _: bool)
142     where M: Mantissa
143 {}}
144 
145 // Shift right N-bytes and round toward zero.
146 //
147 // Floating-point arithmetic defines round toward zero, which rounds
148 // towards positive zero.
149 perftools_inline!{
150 pub(crate) fn round_downward<M>(fp: &mut ExtendedFloat<M>, shift: i32)
151     where M: Mantissa
152 {
153     // Bit shift so the leading bit is in the hidden bit.
154     // No rounding schemes, so we just ignore everything else.
155     let is_truncated = round_toward(fp, shift);
156     downard(fp, is_truncated);
157 }}
158 
159 // NATIVE FLOAT
160 // ------------
161 
162 // FLOAT ROUNDING
163 
164 /// Trait to round extended-precision floats to native representations.
165 pub trait FloatRounding<M: Mantissa>: Float {
166     /// Default number of bits to shift (or 64 - mantissa size - 1).
167     const DEFAULT_SHIFT: i32;
168     /// Mask to determine if a full-carry occurred (1 in bit above hidden bit).
169     const CARRY_MASK: M;
170 }
171 
172 // Literals don't work for generic types, we need to use this as a hack.
173 macro_rules! float_rounding_f32 {
174     ($($t:tt)*) => ($(
175         impl FloatRounding<$t> for f32 {
176             const DEFAULT_SHIFT: i32    = $t::FULL - f32::MANTISSA_SIZE - 1;
177             const CARRY_MASK: $t        = 0x1000000;
178         }
179     )*)
180 }
181 
182 float_rounding_f32! { u64 u128 }
183 
184 // Literals don't work for generic types, we need to use this as a hack.
185 macro_rules! float_rounding_f64 {
186     ($($t:tt)*) => ($(
187         impl FloatRounding<$t> for f64 {
188             const DEFAULT_SHIFT: i32    = $t::FULL - f64::MANTISSA_SIZE - 1;
189             const CARRY_MASK: $t        = 0x20000000000000;
190         }
191     )*)
192 }
193 
194 float_rounding_f64! { u64 u128 }
195 
196 // ROUND TO FLOAT
197 
198 // Shift the ExtendedFloat fraction to the fraction bits in a native float.
199 //
200 // Floating-point arithmetic uses round to nearest, ties to even,
201 // which rounds to the nearest value, if the value is halfway in between,
202 // round to an even value.
203 perftools_inline!{
204 pub(crate) fn round_to_float<T, M, Cb>(fp: &mut ExtendedFloat<M>, cb: Cb)
205     where T: FloatRounding<M>,
206           M: Mantissa,
207           Cb: FnOnce(&mut ExtendedFloat<M>, i32)
208 {
209     // Calculate the difference to allow a single calculation
210     // rather than a loop, to minimize the number of ops required.
211     // This does underflow detection.
212     let final_exp = fp.exp + T::DEFAULT_SHIFT;
213     if final_exp < T::DENORMAL_EXPONENT {
214         // We would end up with a denormal exponent, try to round to more
215         // digits. Only shift right if we can avoid zeroing out the value,
216         // which requires the exponent diff to be < M::BITS. The value
217         // is already normalized, so we shouldn't have any issue zeroing
218         // out the value.
219         let diff = T::DENORMAL_EXPONENT - fp.exp;
220         if diff <= M::FULL {
221             // We can avoid underflow, can get a valid representation.
222             cb(fp, diff);
223         } else {
224             // Certain underflow, assign literal 0s.
225             fp.mant = M::ZERO;
226             fp.exp = 0;
227         }
228     } else {
229         cb(fp, T::DEFAULT_SHIFT);
230     }
231 
232     if fp.mant & T::CARRY_MASK == T::CARRY_MASK {
233         // Roundup carried over to 1 past the hidden bit.
234         shr(fp, 1);
235     }
236 }}
237 
238 // AVOID OVERFLOW/UNDERFLOW
239 
240 // Avoid overflow for large values, shift left as needed.
241 //
242 // Shift until a 1-bit is in the hidden bit, if the mantissa is not 0.
243 perftools_inline!{
244 pub(crate) fn avoid_overflow<T, M>(fp: &mut ExtendedFloat<M>)
245     where T: FloatRounding<M>,
246           M: Mantissa
247 {
248     // Calculate the difference to allow a single calculation
249     // rather than a loop, minimizing the number of ops required.
250     if fp.exp >= T::MAX_EXPONENT {
251         let diff = fp.exp - T::MAX_EXPONENT;
252         if diff <= T::MANTISSA_SIZE {
253             // Our overflow mask needs to start at the hidden bit, or at
254             // `T::MANTISSA_SIZE+1`, and needs to have `diff+1` bits set,
255             // to see if our value overflows.
256             let bit = as_cast(T::MANTISSA_SIZE+1);
257             let n = as_cast(diff+1);
258             let mask: M = internal_n_mask(bit, n);
259             if (fp.mant & mask).is_zero() {
260                 // If we have no 1-bit in the hidden-bit position,
261                 // which is index 0, we need to shift 1.
262                 let shift = diff + 1;
263                 shl(fp, shift);
264             }
265         }
266     }
267 }}
268 
269 // ROUND TO NATIVE
270 
271 // Round an extended-precision float to a native float representation.
272 perftools_inline!{
273 pub(crate) fn round_to_native<T, M, Cb>(fp: &mut ExtendedFloat<M>, cb: Cb)
274     where T: FloatRounding<M>,
275           M: Mantissa,
276           Cb: FnOnce(&mut ExtendedFloat<M>, i32)
277 {
278     // Shift all the way left, to ensure a consistent representation.
279     // The following right-shifts do not work for a non-normalized number.
280     fp.normalize();
281 
282     // Round so the fraction is in a native mantissa representation,
283     // and avoid overflow/underflow.
284     round_to_float::<T, M, _>(fp, cb);
285     avoid_overflow::<T, M>(fp);
286 }}
287 
288 // Get the rounding scheme to determine if we should go up or down.
289 perftools_inline!{
290 #[allow(unused_variables)]
291 pub(crate) fn internal_rounding(kind: RoundingKind, sign: Sign)
292     -> RoundingKind
293 {
294     #[cfg(not(feature = "rounding"))] {
295         RoundingKind::NearestTieEven
296     }
297 
298     #[cfg(feature = "rounding")] {
299         match sign {
300             Sign::Positive => {
301                 match kind {
302                     RoundingKind::TowardPositiveInfinity => RoundingKind::Upward,
303                     RoundingKind::TowardNegativeInfinity => RoundingKind::Downward,
304                     RoundingKind::TowardZero             => RoundingKind::Downward,
305                     _                                    => kind,
306                 }
307             },
308             Sign::Negative => {
309                 match kind {
310                     RoundingKind::TowardPositiveInfinity => RoundingKind::Downward,
311                     RoundingKind::TowardNegativeInfinity => RoundingKind::Upward,
312                     RoundingKind::TowardZero             => RoundingKind::Downward,
313                     _                                    => kind,
314                 }
315             },
316         }
317     }
318 }}
319 
320 // Get the global, default rounding scheme.
321 perftools_inline!{
322 #[cfg(feature = "correct")]
323 #[allow(unused_variables)]
324 pub(crate) fn global_rounding(sign: Sign) -> RoundingKind {
325     #[cfg(not(feature = "rounding"))] {
326         RoundingKind::NearestTieEven
327     }
328 
329     #[cfg(feature = "rounding")] {
330         internal_rounding(get_float_rounding(), sign)
331     }
332 }}
333 
334 // TESTS
335 // -----
336 
337 #[cfg(test)]
338 mod tests {
339     use crate::float::ExtendedFloat80;
340     use super::*;
341 
342     // NEAREST ROUNDING
343 
344     #[test]
round_nearest_test()345     fn round_nearest_test() {
346         // Check exactly halfway (b'1100000')
347         let mut fp = ExtendedFloat80 { mant: 0x60, exp: 0 };
348         let (above, halfway) = round_nearest(&mut fp, 6);
349         assert!(!above);
350         assert!(halfway);
351         assert_eq!(fp.mant, 1);
352 
353         // Check above halfway (b'1100001')
354         let mut fp = ExtendedFloat80 { mant: 0x61, exp: 0 };
355         let (above, halfway) = round_nearest(&mut fp, 6);
356         assert!(above);
357         assert!(!halfway);
358         assert_eq!(fp.mant, 1);
359 
360         // Check below halfway (b'1011111')
361         let mut fp = ExtendedFloat80 { mant: 0x5F, exp: 0 };
362         let (above, halfway) = round_nearest(&mut fp, 6);
363         assert!(!above);
364         assert!(!halfway);
365         assert_eq!(fp.mant, 1);
366     }
367 
368     #[test]
round_nearest_tie_even_test()369     fn round_nearest_tie_even_test() {
370         // Check round-up, halfway
371         let mut fp = ExtendedFloat80 { mant: 0x60, exp: 0 };
372         round_nearest_tie_even(&mut fp, 6);
373         assert_eq!(fp.mant, 2);
374 
375         // Check round-down, halfway
376         let mut fp = ExtendedFloat80 { mant: 0x20, exp: 0 };
377         round_nearest_tie_even(&mut fp, 6);
378         assert_eq!(fp.mant, 0);
379 
380         // Check round-up, above halfway
381         let mut fp = ExtendedFloat80 { mant: 0x61, exp: 0 };
382         round_nearest_tie_even(&mut fp, 6);
383         assert_eq!(fp.mant, 2);
384 
385         let mut fp = ExtendedFloat80 { mant: 0x21, exp: 0 };
386         round_nearest_tie_even(&mut fp, 6);
387         assert_eq!(fp.mant, 1);
388 
389         // Check round-down, below halfway
390         let mut fp = ExtendedFloat80 { mant: 0x5F, exp: 0 };
391         round_nearest_tie_even(&mut fp, 6);
392         assert_eq!(fp.mant, 1);
393 
394         let mut fp = ExtendedFloat80 { mant: 0x1F, exp: 0 };
395         round_nearest_tie_even(&mut fp, 6);
396         assert_eq!(fp.mant, 0);
397     }
398 
399     #[test]
round_nearest_tie_away_zero_test()400     fn round_nearest_tie_away_zero_test() {
401         // Check round-up, halfway
402         let mut fp = ExtendedFloat80 { mant: 0x60, exp: 0 };
403         round_nearest_tie_away_zero(&mut fp, 6);
404         assert_eq!(fp.mant, 2);
405 
406         let mut fp = ExtendedFloat80 { mant: 0x20, exp: 0 };
407         round_nearest_tie_away_zero(&mut fp, 6);
408         assert_eq!(fp.mant, 1);
409 
410         // Check round-up, above halfway
411         let mut fp = ExtendedFloat80 { mant: 0x61, exp: 0 };
412         round_nearest_tie_away_zero(&mut fp, 6);
413         assert_eq!(fp.mant, 2);
414 
415         let mut fp = ExtendedFloat80 { mant: 0x21, exp: 0 };
416         round_nearest_tie_away_zero(&mut fp, 6);
417         assert_eq!(fp.mant, 1);
418 
419         // Check round-down, below halfway
420         let mut fp = ExtendedFloat80 { mant: 0x5F, exp: 0 };
421         round_nearest_tie_away_zero(&mut fp, 6);
422         assert_eq!(fp.mant, 1);
423 
424         let mut fp = ExtendedFloat80 { mant: 0x1F, exp: 0 };
425         round_nearest_tie_away_zero(&mut fp, 6);
426         assert_eq!(fp.mant, 0);
427     }
428 
429     // DIRECTED ROUNDING
430 
431     #[test]
round_upward_test()432     fn round_upward_test() {
433         // b0000000
434         let mut fp = ExtendedFloat80 { mant: 0x00, exp: 0 };
435         round_upward(&mut fp, 6);
436         assert_eq!(fp.mant, 0);
437 
438         // b1000000
439         let mut fp = ExtendedFloat80 { mant: 0x40, exp: 0 };
440         round_upward(&mut fp, 6);
441         assert_eq!(fp.mant, 1);
442 
443         // b1100000
444         let mut fp = ExtendedFloat80 { mant: 0x60, exp: 0 };
445         round_upward(&mut fp, 6);
446         assert_eq!(fp.mant, 2);
447 
448         // b1110000
449         let mut fp = ExtendedFloat80 { mant: 0x70, exp: 0 };
450         round_upward(&mut fp, 6);
451         assert_eq!(fp.mant, 2);
452     }
453 
454     #[test]
round_downward_test()455     fn round_downward_test() {
456         // b0000000
457         let mut fp = ExtendedFloat80 { mant: 0x00, exp: 0 };
458         round_downward(&mut fp, 6);
459         assert_eq!(fp.mant, 0);
460 
461         // b1000000
462         let mut fp = ExtendedFloat80 { mant: 0x40, exp: 0 };
463         round_downward(&mut fp, 6);
464         assert_eq!(fp.mant, 1);
465 
466         // b1100000
467         let mut fp = ExtendedFloat80 { mant: 0x60, exp: 0 };
468         round_downward(&mut fp, 6);
469         assert_eq!(fp.mant, 1);
470 
471         // b1110000
472         let mut fp = ExtendedFloat80 { mant: 0x70, exp: 0 };
473         round_downward(&mut fp, 6);
474         assert_eq!(fp.mant, 1);
475     }
476 
477     // HIGH-LEVEL
478 
479     #[test]
round_to_float_test()480     fn round_to_float_test() {
481         // Denormal
482         let mut fp = ExtendedFloat80 { mant: 1<<63, exp: f64::DENORMAL_EXPONENT - 15 };
483         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
484         assert_eq!(fp.mant, 1<<48);
485         assert_eq!(fp.exp, f64::DENORMAL_EXPONENT);
486 
487         // Halfway, round-down (b'1000000000000000000000000000000000000000000000000000010000000000')
488         let mut fp = ExtendedFloat80 { mant: 0x8000000000000400, exp: -63 };
489         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
490         assert_eq!(fp.mant, 1<<52);
491         assert_eq!(fp.exp, -52);
492 
493         // Halfway, round-up (b'1000000000000000000000000000000000000000000000000000110000000000')
494         let mut fp = ExtendedFloat80 { mant: 0x8000000000000C00, exp: -63 };
495         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
496         assert_eq!(fp.mant, (1<<52) + 2);
497         assert_eq!(fp.exp, -52);
498 
499         // Above halfway
500         let mut fp = ExtendedFloat80 { mant: 0x8000000000000401, exp: -63 };
501         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
502         assert_eq!(fp.mant, (1<<52)+1);
503         assert_eq!(fp.exp, -52);
504 
505         let mut fp = ExtendedFloat80 { mant: 0x8000000000000C01, exp: -63 };
506         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
507         assert_eq!(fp.mant, (1<<52) + 2);
508         assert_eq!(fp.exp, -52);
509 
510         // Below halfway
511         let mut fp = ExtendedFloat80 { mant: 0x80000000000003FF, exp: -63 };
512         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
513         assert_eq!(fp.mant, 1<<52);
514         assert_eq!(fp.exp, -52);
515 
516         let mut fp = ExtendedFloat80 { mant: 0x8000000000000BFF, exp: -63 };
517         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
518         assert_eq!(fp.mant, (1<<52) + 1);
519         assert_eq!(fp.exp, -52);
520     }
521 
522     #[test]
avoid_overflow_test()523     fn avoid_overflow_test() {
524         // Avoid overflow, fails by 1
525         let mut fp = ExtendedFloat80 { mant: 0xFFFFFFFFFFFF, exp: f64::MAX_EXPONENT + 5 };
526         avoid_overflow::<f64, _>(&mut fp);
527         assert_eq!(fp.mant, 0xFFFFFFFFFFFF);
528         assert_eq!(fp.exp, f64::MAX_EXPONENT+5);
529 
530         // Avoid overflow, succeeds
531         let mut fp = ExtendedFloat80 { mant: 0xFFFFFFFFFFFF, exp: f64::MAX_EXPONENT + 4 };
532         avoid_overflow::<f64, _>(&mut fp);
533         assert_eq!(fp.mant, 0x1FFFFFFFFFFFE0);
534         assert_eq!(fp.exp, f64::MAX_EXPONENT-1);
535     }
536 
537     #[test]
round_to_native_test()538     fn round_to_native_test() {
539         // Overflow
540         let mut fp = ExtendedFloat80 { mant: 0xFFFFFFFFFFFF, exp: f64::MAX_EXPONENT + 4 };
541         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
542         assert_eq!(fp.mant, 0x1FFFFFFFFFFFE0);
543         assert_eq!(fp.exp, f64::MAX_EXPONENT-1);
544 
545         // Need denormal
546         let mut fp = ExtendedFloat80 { mant: 1, exp: f64::DENORMAL_EXPONENT +48 };
547         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
548         assert_eq!(fp.mant, 1<<48);
549         assert_eq!(fp.exp, f64::DENORMAL_EXPONENT);
550 
551         // Halfway, round-down (b'10000000000000000000000000000000000000000000000000000100000')
552         let mut fp = ExtendedFloat80 { mant: 0x400000000000020, exp: -58 };
553         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
554         assert_eq!(fp.mant, 1<<52);
555         assert_eq!(fp.exp, -52);
556 
557         // Halfway, round-up (b'10000000000000000000000000000000000000000000000000001100000')
558         let mut fp = ExtendedFloat80 { mant: 0x400000000000060, exp: -58 };
559         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
560         assert_eq!(fp.mant, (1<<52) + 2);
561         assert_eq!(fp.exp, -52);
562 
563         // Above halfway
564         let mut fp = ExtendedFloat80 { mant: 0x400000000000021, exp: -58 };
565         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
566         assert_eq!(fp.mant, (1<<52)+1);
567         assert_eq!(fp.exp, -52);
568 
569         let mut fp = ExtendedFloat80 { mant: 0x400000000000061, exp: -58 };
570         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
571         assert_eq!(fp.mant, (1<<52) + 2);
572         assert_eq!(fp.exp, -52);
573 
574         // Below halfway
575         let mut fp = ExtendedFloat80 { mant: 0x40000000000001F, exp: -58 };
576         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
577         assert_eq!(fp.mant, 1<<52);
578         assert_eq!(fp.exp, -52);
579 
580         let mut fp = ExtendedFloat80 { mant: 0x40000000000005F, exp: -58 };
581         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
582         assert_eq!(fp.mant, (1<<52) + 1);
583         assert_eq!(fp.exp, -52);
584 
585         // Underflow
586         // Adapted from failures in strtod.
587         let mut fp = ExtendedFloat80 { exp: -1139, mant: 18446744073709550712 };
588         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
589         assert_eq!(fp.mant, 0);
590         assert_eq!(fp.exp, 0);
591 
592         let mut fp = ExtendedFloat80 { exp: -1139, mant: 18446744073709551460 };
593         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
594         assert_eq!(fp.mant, 0);
595         assert_eq!(fp.exp, 0);
596 
597         let mut fp = ExtendedFloat80 { exp: -1138, mant: 9223372036854776103 };
598         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
599         assert_eq!(fp.mant, 1);
600         assert_eq!(fp.exp, -1074);
601     }
602 }
603