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