|
| 1 | +//! IEEE 754 floating point compliance tests |
| 2 | +//! |
| 3 | +//! To understand IEEE 754's requirements on a programming language, one must understand that the |
| 4 | +//! requirements of IEEE 754 rest on the total programming environment, and not entirely on any |
| 5 | +//! one component. That means the hardware, language, and even libraries are considered part of |
| 6 | +//! conforming floating point support in a programming environment. |
| 7 | +//! |
| 8 | +//! A programming language's duty, accordingly, is: |
| 9 | +//! 1. offer access to the hardware where the hardware offers support |
| 10 | +//! 2. provide operations that fulfill the remaining requirements of the standard |
| 11 | +//! 3. provide the ability to write additional software that can fulfill those requirements |
| 12 | +//! |
| 13 | +//! This may be fulfilled in any combination that the language sees fit. However, to claim that |
| 14 | +//! a language supports IEEE 754 is to suggest that it has fulfilled requirements 1 and 2, without |
| 15 | +//! deferring minimum requirements to libraries. This is because support for IEEE 754 is defined |
| 16 | +//! as complete support for at least one specified floating point type as an "arithmetic" and |
| 17 | +//! "interchange" format, plus specified type conversions to "external character sequences" and |
| 18 | +//! integer types. |
| 19 | +//! |
| 20 | +//! For our purposes, |
| 21 | +//! "interchange format" => f32, f64 |
| 22 | +//! "arithmetic format" => f32, f64, and any "soft floats" |
| 23 | +//! "external character sequence" => str from any float |
| 24 | +//! "integer format" => {i,u}{8,16,32,64,128} |
| 25 | +//! |
| 26 | +//! None of these tests are against Rust's own implementation. They are only tests against the |
| 27 | +//! standard. That is why they accept wildly diverse inputs or may seem to duplicate other tests. |
| 28 | +//! Please consider this carefully when adding, removing, or reorganizing these tests. They are |
| 29 | +//! here so that it is clear what tests are required by the standard and what can be changed. |
| 30 | +use ::core::str::FromStr; |
| 31 | + |
| 32 | +// IEEE 754 for many tests is applied to specific bit patterns. |
| 33 | +// These generally are not applicable to NaN, however. |
| 34 | +macro_rules! assert_biteq { |
| 35 | + ($lhs:expr, $rhs:expr) => { |
| 36 | + assert_eq!($lhs.to_bits(), $rhs.to_bits()) |
| 37 | + }; |
| 38 | +} |
| 39 | + |
| 40 | +// ToString uses the default fmt::Display impl without special concerns, and bypasses other parts |
| 41 | +// of the formatting infrastructure, which makes it ideal for testing here. |
| 42 | +#[allow(unused_macros)] |
| 43 | +macro_rules! roundtrip { |
| 44 | + ($f:expr => $t:ty) => { |
| 45 | + ($f).to_string().parse::<$t>().unwrap() |
| 46 | + }; |
| 47 | +} |
| 48 | + |
| 49 | +macro_rules! assert_floats_roundtrip { |
| 50 | + ($f:ident) => { |
| 51 | + assert_biteq!(f32::$f, roundtrip!(f32::$f => f32)); |
| 52 | + assert_biteq!(f64::$f, roundtrip!(f64::$f => f64)); |
| 53 | + }; |
| 54 | + ($f:expr) => { |
| 55 | + assert_biteq!($f as f32, roundtrip!($f => f32)); |
| 56 | + assert_biteq!($f as f64, roundtrip!($f => f64)); |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +macro_rules! assert_floats_bitne { |
| 61 | + ($lhs:ident, $rhs:ident) => { |
| 62 | + assert_ne!(f32::$lhs.to_bits(), f32::$rhs.to_bits()); |
| 63 | + assert_ne!(f64::$lhs.to_bits(), f64::$rhs.to_bits()); |
| 64 | + }; |
| 65 | + ($lhs:expr, $rhs:expr) => { |
| 66 | + assert_ne!(f32::to_bits($lhs), f32::to_bits($rhs)); |
| 67 | + assert_ne!(f64::to_bits($lhs), f64::to_bits($rhs)); |
| 68 | + }; |
| 69 | +} |
| 70 | + |
| 71 | +// We must preserve signs on all numbers. That includes zero. |
| 72 | +// -0 and 0 are == normally, so test bit equality. |
| 73 | +#[test] |
| 74 | +fn preserve_signed_zero() { |
| 75 | + assert_floats_roundtrip!(-0.0); |
| 76 | + assert_floats_roundtrip!(0.0); |
| 77 | + assert_floats_bitne!(0.0, -0.0); |
| 78 | +} |
| 79 | + |
| 80 | +#[test] |
| 81 | +fn preserve_signed_infinity() { |
| 82 | + assert_floats_roundtrip!(INFINITY); |
| 83 | + assert_floats_roundtrip!(NEG_INFINITY); |
| 84 | + assert_floats_bitne!(INFINITY, NEG_INFINITY); |
| 85 | +} |
| 86 | + |
| 87 | +#[test] |
| 88 | +fn infinity_to_str() { |
| 89 | + assert!(match f32::INFINITY.to_string().to_lowercase().as_str() { |
| 90 | + "+infinity" | "infinity" => true, |
| 91 | + "+inf" | "inf" => true, |
| 92 | + _ => false, |
| 93 | + }); |
| 94 | + assert!( |
| 95 | + match f64::INFINITY.to_string().to_lowercase().as_str() { |
| 96 | + "+infinity" | "infinity" => true, |
| 97 | + "+inf" | "inf" => true, |
| 98 | + _ => false, |
| 99 | + }, |
| 100 | + "Infinity must write to a string as some casing of inf or infinity, with an optional +." |
| 101 | + ); |
| 102 | +} |
| 103 | + |
| 104 | +#[test] |
| 105 | +fn neg_infinity_to_str() { |
| 106 | + assert!(match f32::NEG_INFINITY.to_string().to_lowercase().as_str() { |
| 107 | + "-infinity" | "-inf" => true, |
| 108 | + _ => false, |
| 109 | + }); |
| 110 | + assert!( |
| 111 | + match f64::NEG_INFINITY.to_string().to_lowercase().as_str() { |
| 112 | + "-infinity" | "-inf" => true, |
| 113 | + _ => false, |
| 114 | + }, |
| 115 | + "Negative Infinity must write to a string as some casing of -inf or -infinity" |
| 116 | + ) |
| 117 | +} |
| 118 | + |
| 119 | +#[test] |
| 120 | +fn nan_to_str() { |
| 121 | + assert!( |
| 122 | + match f32::NAN.to_string().to_lowercase().as_str() { |
| 123 | + "nan" | "+nan" | "-nan" => true, |
| 124 | + _ => false, |
| 125 | + }, |
| 126 | + "NaNs must write to a string as some casing of nan." |
| 127 | + ) |
| 128 | +} |
| 129 | + |
| 130 | +// "+"?("inf"|"infinity") in any case => Infinity |
| 131 | +#[test] |
| 132 | +fn infinity_from_str() { |
| 133 | + assert_biteq!(f32::INFINITY, f32::from_str("infinity").unwrap()); |
| 134 | + assert_biteq!(f32::INFINITY, f32::from_str("inf").unwrap()); |
| 135 | + assert_biteq!(f32::INFINITY, f32::from_str("+infinity").unwrap()); |
| 136 | + assert_biteq!(f32::INFINITY, f32::from_str("+inf").unwrap()); |
| 137 | + // yes! this means you are weLcOmE tO mY iNfInItElY tWiStEd MiNd |
| 138 | + assert_biteq!(f32::INFINITY, f32::from_str("+iNfInItY").unwrap()); |
| 139 | +} |
| 140 | + |
| 141 | +// "-inf"|"-infinity" in any case => Negative Infinity |
| 142 | +#[test] |
| 143 | +fn neg_infinity_from_str() { |
| 144 | + assert_biteq!(f32::NEG_INFINITY, f32::from_str("-infinity").unwrap()); |
| 145 | + assert_biteq!(f32::NEG_INFINITY, f32::from_str("-inf").unwrap()); |
| 146 | + assert_biteq!(f32::NEG_INFINITY, f32::from_str("-INF").unwrap()); |
| 147 | + assert_biteq!(f32::NEG_INFINITY, f32::from_str("-INFinity").unwrap()); |
| 148 | +} |
| 149 | + |
| 150 | +// ("+"|"-"")?"s"?"nan" in any case => qNaN |
| 151 | +#[test] |
| 152 | +fn qnan_from_str() { |
| 153 | + assert!("nan".parse::<f32>().unwrap().is_nan()); |
| 154 | + assert!("-nan".parse::<f32>().unwrap().is_nan()); |
| 155 | + assert!("+nan".parse::<f32>().unwrap().is_nan()); |
| 156 | + assert!("+NAN".parse::<f32>().unwrap().is_nan()); |
| 157 | + assert!("-NaN".parse::<f32>().unwrap().is_nan()); |
| 158 | +} |
0 commit comments