oxedyne/fe2o3/fe2o3_units/tests/rounding.rs
4.9 KiB, 1 run
created by r1870400018:17549, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | //! External-oracle tests for significant figure rounding. |
| 2 | //! |
| 3 | //! Every expected value here is worked out by hand from the decimal digits and |
| 4 | //! not read off the implementation. Write the magnitude as `d.ddd x 10^k`, keep |
| 5 | //! the tracked number of digits of the significand, and round the last of them |
| 6 | //! half away from zero. A sign plays no part in that: the digits kept for |
| 7 | //! -0.475 are the digits kept for 0.475. |
| 8 | |
| 9 | use oxedyne_fe2o3_units::{ |
| 10 | dimension::Dimension, |
| 11 | quantity::Quantity, |
| 12 | }; |
| 13 | |
| 14 | use oxedyne_fe2o3_core::prelude::*; |
| 15 | |
| 16 | #[test] |
| 17 | fn test_rounded_above_one_01() -> Outcome<()> { |
| 18 | // 1234 m is 1.234 x 10^3 m; three figures keep 1.23, so 1230 m. |
| 19 | assert_eq!(res!(Quantity::metres(1234.0, 3)).rounded(), 1230.0); |
| 20 | // 1236 m rounds the third figure up, so 1240 m. |
| 21 | assert_eq!(res!(Quantity::metres(1236.0, 3)).rounded(), 1240.0); |
| 22 | // 98765 s is 9.8765 x 10^4 s; two figures keep 9.9, so 99000 s. |
| 23 | assert_eq!(res!(Quantity::seconds(98765.0, 2)).rounded(), 99000.0); |
| 24 | // 9.99 kg to two figures carries into a new decade: 10 kg. |
| 25 | assert_eq!(res!(Quantity::kilograms(9.99, 2)).rounded(), 10.0); |
| 26 | Ok(()) |
| 27 | } |
| 28 | |
| 29 | #[test] |
| 30 | fn test_rounded_negative_above_one_01() -> Outcome<()> { |
| 31 | assert_eq!(res!(Quantity::metres(-1234.0, 3)).rounded(), -1230.0); |
| 32 | assert_eq!(res!(Quantity::metres(-1236.0, 3)).rounded(), -1240.0); |
| 33 | assert_eq!(res!(Quantity::seconds(-98765.0, 2)).rounded(), -99000.0); |
| 34 | assert_eq!(res!(Quantity::kilograms(-9.99, 2)).rounded(), -10.0); |
| 35 | Ok(()) |
| 36 | } |
| 37 | |
| 38 | #[test] |
| 39 | fn test_rounded_below_one_01() -> Outcome<()> { |
| 40 | // 0.4749 is 4.749 x 10^-1; two figures keep 4.7. |
| 41 | assert_eq!(res!(Quantity::dimensionless(0.4749, 2)).rounded(), 0.47); |
| 42 | // 0.4751 rounds the second figure up. |
| 43 | assert_eq!(res!(Quantity::dimensionless(0.4751, 2)).rounded(), 0.48); |
| 44 | // 0.05512 is 5.512 x 10^-2; two figures keep 5.5. |
| 45 | assert_eq!(res!(Quantity::dimensionless(0.05512, 2)).rounded(), 0.055); |
| 46 | // 0.9996 carries through the decade to 1.00. |
| 47 | assert_eq!(res!(Quantity::dimensionless(0.9996, 3)).rounded(), 1.0); |
| 48 | Ok(()) |
| 49 | } |
| 50 | |
| 51 | /// A slope is dimensionless and usually smaller than one, which is the case |
| 52 | /// that went wrong: a gradient of -0.475 read to two figures is -0.48, and one |
| 53 | /// of -0.5496 is -0.55. |
| 54 | #[test] |
| 55 | fn test_rounded_negative_below_one_01() -> Outcome<()> { |
| 56 | assert_eq!(res!(Quantity::dimensionless(-0.475, 2)).rounded(), -0.48); |
| 57 | assert_eq!(res!(Quantity::dimensionless(-0.5496, 2)).rounded(), -0.55); |
| 58 | assert_eq!(res!(Quantity::dimensionless(-0.055, 2)).rounded(), -0.055); |
| 59 | assert_eq!(res!(Quantity::dimensionless(-0.4749, 2)).rounded(), -0.47); |
| 60 | assert_eq!(res!(Quantity::dimensionless(-0.0999, 2)).rounded(), -0.1); |
| 61 | Ok(()) |
| 62 | } |
| 63 | |
| 64 | /// A gradient formed by dividing a rise by a run keeps the coarser figure |
| 65 | /// count and rounds by the same rule as a value entered directly. |
| 66 | #[test] |
| 67 | fn test_rounded_slope_from_division_01() -> Outcome<()> { |
| 68 | // -0.95 m over 2.0 m is -0.475, dimensionless, to two figures: -0.48. |
| 69 | let rise = res!(Quantity::metres(-0.95, 2)); |
| 70 | let run = res!(Quantity::metres(2.0, 3)); |
| 71 | let slope = rise.div(&run); |
| 72 | assert_eq!(slope.sf(), 2); |
| 73 | assert!(slope.dim().is_dimensionless()); |
| 74 | assert_eq!(slope.rounded(), -0.48); |
| 75 | Ok(()) |
| 76 | } |
| 77 | |
| 78 | /// An exact power of ten is already at one figure, so it survives any count |
| 79 | /// unchanged, either sign. |
| 80 | #[test] |
| 81 | fn test_rounded_powers_of_ten_01() -> Outcome<()> { |
| 82 | for exp in -18i32..=18 { |
| 83 | let v = match format!("1e{}", exp).parse::<f64>() { |
| 84 | Ok(v) => v, |
| 85 | Err(_) => continue, |
| 86 | }; |
| 87 | for sf in 1..=5u8 { |
| 88 | assert_eq!(res!(Quantity::dimensionless(v, sf)).rounded(), v, |
| 89 | "10^{} at {} sf", exp, sf); |
| 90 | assert_eq!(res!(Quantity::dimensionless(-v, sf)).rounded(), -v, |
| 91 | "-10^{} at {} sf", exp, sf); |
| 92 | } |
| 93 | } |
| 94 | Ok(()) |
| 95 | } |
| 96 | |
| 97 | /// A tie is settled away from zero, so 0.125 to two figures is 0.13 and 2.5 to |
| 98 | /// one figure is 3. |
| 99 | #[test] |
| 100 | fn test_rounded_ties_away_from_zero_01() -> Outcome<()> { |
| 101 | assert_eq!(res!(Quantity::dimensionless(0.125, 2)).rounded(), 0.13); |
| 102 | assert_eq!(res!(Quantity::dimensionless(-0.125, 2)).rounded(), -0.13); |
| 103 | assert_eq!(res!(Quantity::dimensionless(2.5, 1)).rounded(), 3.0); |
| 104 | assert_eq!(res!(Quantity::dimensionless(-2.5, 1)).rounded(), -3.0); |
| 105 | Ok(()) |
| 106 | } |
| 107 | |
| 108 | /// A zero carries no scale, so it rounds to zero rather than to a |
| 109 | /// not-a-number. |
| 110 | #[test] |
| 111 | fn test_rounded_zero_01() -> Outcome<()> { |
| 112 | assert_eq!(res!(Quantity::dimensionless(0.0, 3)).rounded(), 0.0); |
| 113 | assert_eq!(res!(Quantity::new(0.0, 1, Dimension::force())).rounded(), 0.0); |
| 114 | Ok(()) |
| 115 | } |
| 116 | |
| 117 | /// The displayed form uses the rounded magnitude, so a negative slope reads |
| 118 | /// with the figures it was measured to. |
| 119 | #[test] |
| 120 | fn test_display_uses_rounded_magnitude_01() -> Outcome<()> { |
| 121 | let q = res!(Quantity::dimensionless(-0.475, 2)); |
| 122 | assert!(format!("{}", q).starts_with("-0.48"), "displayed as {}", q); |
| 123 | Ok(()) |
| 124 | } |