1 //! Defines rounding schemes for floating-point numbers.
2 
3 use 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 #[cfg(has_i128)]
183 float_rounding_f32! { u64 u128 }
184 
185 #[cfg(not(has_i128))]
186 float_rounding_f32! { u64 }
187 
188 // Literals don't work for generic types, we need to use this as a hack.
189 macro_rules! float_rounding_f64 {
190     ($($t:tt)*) => ($(
191         impl FloatRounding<$t> for f64 {
192             const DEFAULT_SHIFT: i32    = $t::FULL - f64::MANTISSA_SIZE - 1;
193             const CARRY_MASK: $t        = 0x20000000000000;
194         }
195     )*)
196 }
197 
198 #[cfg(has_i128)]
199 float_rounding_f64! { u64 u128 }
200 
201 #[cfg(not(has_i128))]
202 float_rounding_f64! { u64 }
203 
204 // ROUND TO FLOAT
205 
206 // Shift the ExtendedFloat fraction to the fraction bits in a native float.
207 //
208 // Floating-point arithmetic uses round to nearest, ties to even,
209 // which rounds to the nearest value, if the value is halfway in between,
210 // round to an even value.
211 perftools_inline!{
212 pub(crate) fn round_to_float<T, M, Cb>(fp: &mut ExtendedFloat<M>, cb: Cb)
213     where T: FloatRounding<M>,
214           M: Mantissa,
215           Cb: FnOnce(&mut ExtendedFloat<M>, i32)
216 {
217     // Calculate the difference to allow a single calculation
218     // rather than a loop, to minimize the number of ops required.
219     // This does underflow detection.
220     let final_exp = fp.exp + T::DEFAULT_SHIFT;
221     if final_exp < T::DENORMAL_EXPONENT {
222         // We would end up with a denormal exponent, try to round to more
223         // digits. Only shift right if we can avoid zeroing out the value,
224         // which requires the exponent diff to be < M::BITS. The value
225         // is already normalized, so we shouldn't have any issue zeroing
226         // out the value.
227         let diff = T::DENORMAL_EXPONENT - fp.exp;
228         if diff <= M::FULL {
229             // We can avoid underflow, can get a valid representation.
230             cb(fp, diff);
231         } else {
232             // Certain underflow, assign literal 0s.
233             fp.mant = M::ZERO;
234             fp.exp = 0;
235         }
236     } else {
237         cb(fp, T::DEFAULT_SHIFT);
238     }
239 
240     if fp.mant & T::CARRY_MASK == T::CARRY_MASK {
241         // Roundup carried over to 1 past the hidden bit.
242         shr(fp, 1);
243     }
244 }}
245 
246 // AVOID OVERFLOW/UNDERFLOW
247 
248 // Avoid overflow for large values, shift left as needed.
249 //
250 // Shift until a 1-bit is in the hidden bit, if the mantissa is not 0.
251 perftools_inline!{
252 pub(crate) fn avoid_overflow<T, M>(fp: &mut ExtendedFloat<M>)
253     where T: FloatRounding<M>,
254           M: Mantissa
255 {
256     // Calculate the difference to allow a single calculation
257     // rather than a loop, minimizing the number of ops required.
258     if fp.exp >= T::MAX_EXPONENT {
259         let diff = fp.exp - T::MAX_EXPONENT;
260         if diff <= T::MANTISSA_SIZE {
261             // Our overflow mask needs to start at the hidden bit, or at
262             // `T::MANTISSA_SIZE+1`, and needs to have `diff+1` bits set,
263             // to see if our value overflows.
264             let bit = as_cast(T::MANTISSA_SIZE+1);
265             let n = as_cast(diff+1);
266             let mask: M = internal_n_mask(bit, n);
267             if (fp.mant & mask).is_zero() {
268                 // If we have no 1-bit in the hidden-bit position,
269                 // which is index 0, we need to shift 1.
270                 let shift = diff + 1;
271                 shl(fp, shift);
272             }
273         }
274     }
275 }}
276 
277 // ROUND TO NATIVE
278 
279 // Round an extended-precision float to a native float representation.
280 perftools_inline!{
281 pub(crate) fn round_to_native<T, M, Cb>(fp: &mut ExtendedFloat<M>, cb: Cb)
282     where T: FloatRounding<M>,
283           M: Mantissa,
284           Cb: FnOnce(&mut ExtendedFloat<M>, i32)
285 {
286     // Shift all the way left, to ensure a consistent representation.
287     // The following right-shifts do not work for a non-normalized number.
288     fp.normalize();
289 
290     // Round so the fraction is in a native mantissa representation,
291     // and avoid overflow/underflow.
292     round_to_float::<T, M, _>(fp, cb);
293     avoid_overflow::<T, M>(fp);
294 }}
295 
296 // Get the rounding scheme to determine if we should go up or down.
297 perftools_inline!{
298 #[allow(unused_variables)]
299 pub(crate) fn internal_rounding(kind: RoundingKind, sign: Sign)
300     -> RoundingKind
301 {
302     #[cfg(not(feature = "rounding"))] {
303         RoundingKind::NearestTieEven
304     }
305 
306     #[cfg(feature = "rounding")] {
307         match sign {
308             Sign::Positive => {
309                 match kind {
310                     RoundingKind::TowardPositiveInfinity => RoundingKind::Upward,
311                     RoundingKind::TowardNegativeInfinity => RoundingKind::Downward,
312                     RoundingKind::TowardZero             => RoundingKind::Downward,
313                     _                                    => kind,
314                 }
315             },
316             Sign::Negative => {
317                 match kind {
318                     RoundingKind::TowardPositiveInfinity => RoundingKind::Downward,
319                     RoundingKind::TowardNegativeInfinity => RoundingKind::Upward,
320                     RoundingKind::TowardZero             => RoundingKind::Downward,
321                     _                                    => kind,
322                 }
323             },
324         }
325     }
326 }}
327 
328 // Get the global, default rounding scheme.
329 perftools_inline!{
330 #[cfg(feature = "correct")]
331 #[allow(unused_variables)]
332 pub(crate) fn global_rounding(sign: Sign) -> RoundingKind {
333     #[cfg(not(feature = "rounding"))] {
334         RoundingKind::NearestTieEven
335     }
336 
337     #[cfg(feature = "rounding")] {
338         internal_rounding(get_float_rounding(), sign)
339     }
340 }}
341 
342 // TESTS
343 // -----
344 
345 #[cfg(test)]
346 mod tests {
347     use float::ExtendedFloat80;
348     use super::*;
349 
350     // NEAREST ROUNDING
351 
352     #[test]
round_nearest_test()353     fn round_nearest_test() {
354         // Check exactly halfway (b'1100000')
355         let mut fp = ExtendedFloat80 { mant: 0x60, exp: 0 };
356         let (above, halfway) = round_nearest(&mut fp, 6);
357         assert!(!above);
358         assert!(halfway);
359         assert_eq!(fp.mant, 1);
360 
361         // Check above halfway (b'1100001')
362         let mut fp = ExtendedFloat80 { mant: 0x61, exp: 0 };
363         let (above, halfway) = round_nearest(&mut fp, 6);
364         assert!(above);
365         assert!(!halfway);
366         assert_eq!(fp.mant, 1);
367 
368         // Check below halfway (b'1011111')
369         let mut fp = ExtendedFloat80 { mant: 0x5F, exp: 0 };
370         let (above, halfway) = round_nearest(&mut fp, 6);
371         assert!(!above);
372         assert!(!halfway);
373         assert_eq!(fp.mant, 1);
374     }
375 
376     #[test]
round_nearest_tie_even_test()377     fn round_nearest_tie_even_test() {
378         // Check round-up, halfway
379         let mut fp = ExtendedFloat80 { mant: 0x60, exp: 0 };
380         round_nearest_tie_even(&mut fp, 6);
381         assert_eq!(fp.mant, 2);
382 
383         // Check round-down, halfway
384         let mut fp = ExtendedFloat80 { mant: 0x20, exp: 0 };
385         round_nearest_tie_even(&mut fp, 6);
386         assert_eq!(fp.mant, 0);
387 
388         // Check round-up, above halfway
389         let mut fp = ExtendedFloat80 { mant: 0x61, exp: 0 };
390         round_nearest_tie_even(&mut fp, 6);
391         assert_eq!(fp.mant, 2);
392 
393         let mut fp = ExtendedFloat80 { mant: 0x21, exp: 0 };
394         round_nearest_tie_even(&mut fp, 6);
395         assert_eq!(fp.mant, 1);
396 
397         // Check round-down, below halfway
398         let mut fp = ExtendedFloat80 { mant: 0x5F, exp: 0 };
399         round_nearest_tie_even(&mut fp, 6);
400         assert_eq!(fp.mant, 1);
401 
402         let mut fp = ExtendedFloat80 { mant: 0x1F, exp: 0 };
403         round_nearest_tie_even(&mut fp, 6);
404         assert_eq!(fp.mant, 0);
405     }
406 
407     #[test]
round_nearest_tie_away_zero_test()408     fn round_nearest_tie_away_zero_test() {
409         // Check round-up, halfway
410         let mut fp = ExtendedFloat80 { mant: 0x60, exp: 0 };
411         round_nearest_tie_away_zero(&mut fp, 6);
412         assert_eq!(fp.mant, 2);
413 
414         let mut fp = ExtendedFloat80 { mant: 0x20, exp: 0 };
415         round_nearest_tie_away_zero(&mut fp, 6);
416         assert_eq!(fp.mant, 1);
417 
418         // Check round-up, above halfway
419         let mut fp = ExtendedFloat80 { mant: 0x61, exp: 0 };
420         round_nearest_tie_away_zero(&mut fp, 6);
421         assert_eq!(fp.mant, 2);
422 
423         let mut fp = ExtendedFloat80 { mant: 0x21, exp: 0 };
424         round_nearest_tie_away_zero(&mut fp, 6);
425         assert_eq!(fp.mant, 1);
426 
427         // Check round-down, below halfway
428         let mut fp = ExtendedFloat80 { mant: 0x5F, exp: 0 };
429         round_nearest_tie_away_zero(&mut fp, 6);
430         assert_eq!(fp.mant, 1);
431 
432         let mut fp = ExtendedFloat80 { mant: 0x1F, exp: 0 };
433         round_nearest_tie_away_zero(&mut fp, 6);
434         assert_eq!(fp.mant, 0);
435     }
436 
437     // DIRECTED ROUNDING
438 
439     #[test]
round_upward_test()440     fn round_upward_test() {
441         // b0000000
442         let mut fp = ExtendedFloat80 { mant: 0x00, exp: 0 };
443         round_upward(&mut fp, 6);
444         assert_eq!(fp.mant, 0);
445 
446         // b1000000
447         let mut fp = ExtendedFloat80 { mant: 0x40, exp: 0 };
448         round_upward(&mut fp, 6);
449         assert_eq!(fp.mant, 1);
450 
451         // b1100000
452         let mut fp = ExtendedFloat80 { mant: 0x60, exp: 0 };
453         round_upward(&mut fp, 6);
454         assert_eq!(fp.mant, 2);
455 
456         // b1110000
457         let mut fp = ExtendedFloat80 { mant: 0x70, exp: 0 };
458         round_upward(&mut fp, 6);
459         assert_eq!(fp.mant, 2);
460     }
461 
462     #[test]
round_downward_test()463     fn round_downward_test() {
464         // b0000000
465         let mut fp = ExtendedFloat80 { mant: 0x00, exp: 0 };
466         round_downward(&mut fp, 6);
467         assert_eq!(fp.mant, 0);
468 
469         // b1000000
470         let mut fp = ExtendedFloat80 { mant: 0x40, exp: 0 };
471         round_downward(&mut fp, 6);
472         assert_eq!(fp.mant, 1);
473 
474         // b1100000
475         let mut fp = ExtendedFloat80 { mant: 0x60, exp: 0 };
476         round_downward(&mut fp, 6);
477         assert_eq!(fp.mant, 1);
478 
479         // b1110000
480         let mut fp = ExtendedFloat80 { mant: 0x70, exp: 0 };
481         round_downward(&mut fp, 6);
482         assert_eq!(fp.mant, 1);
483     }
484 
485     // HIGH-LEVEL
486 
487     #[test]
round_to_float_test()488     fn round_to_float_test() {
489         // Denormal
490         let mut fp = ExtendedFloat80 { mant: 1<<63, exp: f64::DENORMAL_EXPONENT - 15 };
491         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
492         assert_eq!(fp.mant, 1<<48);
493         assert_eq!(fp.exp, f64::DENORMAL_EXPONENT);
494 
495         // Halfway, round-down (b'1000000000000000000000000000000000000000000000000000010000000000')
496         let mut fp = ExtendedFloat80 { mant: 0x8000000000000400, exp: -63 };
497         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
498         assert_eq!(fp.mant, 1<<52);
499         assert_eq!(fp.exp, -52);
500 
501         // Halfway, round-up (b'1000000000000000000000000000000000000000000000000000110000000000')
502         let mut fp = ExtendedFloat80 { mant: 0x8000000000000C00, exp: -63 };
503         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
504         assert_eq!(fp.mant, (1<<52) + 2);
505         assert_eq!(fp.exp, -52);
506 
507         // Above halfway
508         let mut fp = ExtendedFloat80 { mant: 0x8000000000000401, exp: -63 };
509         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
510         assert_eq!(fp.mant, (1<<52)+1);
511         assert_eq!(fp.exp, -52);
512 
513         let mut fp = ExtendedFloat80 { mant: 0x8000000000000C01, exp: -63 };
514         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
515         assert_eq!(fp.mant, (1<<52) + 2);
516         assert_eq!(fp.exp, -52);
517 
518         // Below halfway
519         let mut fp = ExtendedFloat80 { mant: 0x80000000000003FF, exp: -63 };
520         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
521         assert_eq!(fp.mant, 1<<52);
522         assert_eq!(fp.exp, -52);
523 
524         let mut fp = ExtendedFloat80 { mant: 0x8000000000000BFF, exp: -63 };
525         round_to_float::<f64, _, _>(&mut fp, round_nearest_tie_even);
526         assert_eq!(fp.mant, (1<<52) + 1);
527         assert_eq!(fp.exp, -52);
528     }
529 
530     #[test]
avoid_overflow_test()531     fn avoid_overflow_test() {
532         // Avoid overflow, fails by 1
533         let mut fp = ExtendedFloat80 { mant: 0xFFFFFFFFFFFF, exp: f64::MAX_EXPONENT + 5 };
534         avoid_overflow::<f64, _>(&mut fp);
535         assert_eq!(fp.mant, 0xFFFFFFFFFFFF);
536         assert_eq!(fp.exp, f64::MAX_EXPONENT+5);
537 
538         // Avoid overflow, succeeds
539         let mut fp = ExtendedFloat80 { mant: 0xFFFFFFFFFFFF, exp: f64::MAX_EXPONENT + 4 };
540         avoid_overflow::<f64, _>(&mut fp);
541         assert_eq!(fp.mant, 0x1FFFFFFFFFFFE0);
542         assert_eq!(fp.exp, f64::MAX_EXPONENT-1);
543     }
544 
545     #[test]
round_to_native_test()546     fn round_to_native_test() {
547         // Overflow
548         let mut fp = ExtendedFloat80 { mant: 0xFFFFFFFFFFFF, exp: f64::MAX_EXPONENT + 4 };
549         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
550         assert_eq!(fp.mant, 0x1FFFFFFFFFFFE0);
551         assert_eq!(fp.exp, f64::MAX_EXPONENT-1);
552 
553         // Need denormal
554         let mut fp = ExtendedFloat80 { mant: 1, exp: f64::DENORMAL_EXPONENT +48 };
555         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
556         assert_eq!(fp.mant, 1<<48);
557         assert_eq!(fp.exp, f64::DENORMAL_EXPONENT);
558 
559         // Halfway, round-down (b'10000000000000000000000000000000000000000000000000000100000')
560         let mut fp = ExtendedFloat80 { mant: 0x400000000000020, exp: -58 };
561         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
562         assert_eq!(fp.mant, 1<<52);
563         assert_eq!(fp.exp, -52);
564 
565         // Halfway, round-up (b'10000000000000000000000000000000000000000000000000001100000')
566         let mut fp = ExtendedFloat80 { mant: 0x400000000000060, exp: -58 };
567         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
568         assert_eq!(fp.mant, (1<<52) + 2);
569         assert_eq!(fp.exp, -52);
570 
571         // Above halfway
572         let mut fp = ExtendedFloat80 { mant: 0x400000000000021, exp: -58 };
573         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
574         assert_eq!(fp.mant, (1<<52)+1);
575         assert_eq!(fp.exp, -52);
576 
577         let mut fp = ExtendedFloat80 { mant: 0x400000000000061, exp: -58 };
578         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
579         assert_eq!(fp.mant, (1<<52) + 2);
580         assert_eq!(fp.exp, -52);
581 
582         // Below halfway
583         let mut fp = ExtendedFloat80 { mant: 0x40000000000001F, exp: -58 };
584         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
585         assert_eq!(fp.mant, 1<<52);
586         assert_eq!(fp.exp, -52);
587 
588         let mut fp = ExtendedFloat80 { mant: 0x40000000000005F, exp: -58 };
589         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
590         assert_eq!(fp.mant, (1<<52) + 1);
591         assert_eq!(fp.exp, -52);
592 
593         // Underflow
594         // Adapted from failures in strtod.
595         let mut fp = ExtendedFloat80 { exp: -1139, mant: 18446744073709550712 };
596         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
597         assert_eq!(fp.mant, 0);
598         assert_eq!(fp.exp, 0);
599 
600         let mut fp = ExtendedFloat80 { exp: -1139, mant: 18446744073709551460 };
601         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
602         assert_eq!(fp.mant, 0);
603         assert_eq!(fp.exp, 0);
604 
605         let mut fp = ExtendedFloat80 { exp: -1138, mant: 9223372036854776103 };
606         round_to_native::<f64, _, _>(&mut fp, round_nearest_tie_even);
607         assert_eq!(fp.mant, 1);
608         assert_eq!(fp.exp, -1074);
609     }
610 }
611