about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/core/tests/num/ieee754.rs158
-rw-r--r--library/core/tests/num/mod.rs1
2 files changed, 159 insertions, 0 deletions
diff --git a/library/core/tests/num/ieee754.rs b/library/core/tests/num/ieee754.rs
new file mode 100644
index 00000000000..f6e5dfc98c7
--- /dev/null
+++ b/library/core/tests/num/ieee754.rs
@@ -0,0 +1,158 @@
+//! IEEE 754 floating point compliance tests
+//!
+//! To understand IEEE 754's requirements on a programming language, one must understand that the
+//! requirements of IEEE 754 rest on the total programming environment, and not entirely on any
+//! one component. That means the hardware, language, and even libraries are considered part of
+//! conforming floating point support in a programming environment.
+//!
+//! A programming language's duty, accordingly, is:
+//!   1. offer access to the hardware where the hardware offers support
+//!   2. provide operations that fulfill the remaining requirements of the standard
+//!   3. provide the ability to write additional software that can fulfill those requirements
+//!
+//! This may be fulfilled in any combination that the language sees fit. However, to claim that
+//! a language supports IEEE 754 is to suggest that it has fulfilled requirements 1 and 2, without
+//! deferring minimum requirements to libraries. This is because support for IEEE 754 is defined
+//! as complete support for at least one specified floating point type as an "arithmetic" and
+//! "interchange" format, plus specified type conversions to "external character sequences" and
+//! integer types.
+//!
+//! For our purposes,
+//! "interchange format"          => f32, f64
+//! "arithmetic format"           => f32, f64, and any "soft floats"
+//! "external character sequence" => str from any float
+//! "integer format"              => {i,u}{8,16,32,64,128}
+//!
+//! None of these tests are against Rust's own implementation. They are only tests against the
+//! standard. That is why they accept wildly diverse inputs or may seem to duplicate other tests.
+//! Please consider this carefully when adding, removing, or reorganizing these tests. They are
+//! here so that it is clear what tests are required by the standard and what can be changed.
+use ::core::str::FromStr;
+
+// IEEE 754 for many tests is applied to specific bit patterns.
+// These generally are not applicable to NaN, however.
+macro_rules! assert_biteq {
+    ($lhs:expr, $rhs:expr) => {
+        assert_eq!($lhs.to_bits(), $rhs.to_bits())
+    };
+}
+
+// ToString uses the default fmt::Display impl without special concerns, and bypasses other parts
+// of the formatting infrastructure, which makes it ideal for testing here.
+#[allow(unused_macros)]
+macro_rules! roundtrip {
+    ($f:expr => $t:ty) => {
+        ($f).to_string().parse::<$t>().unwrap()
+    };
+}
+
+macro_rules! assert_floats_roundtrip {
+    ($f:ident) => {
+        assert_biteq!(f32::$f, roundtrip!(f32::$f => f32));
+        assert_biteq!(f64::$f, roundtrip!(f64::$f => f64));
+    };
+    ($f:expr) => {
+        assert_biteq!($f as f32, roundtrip!($f => f32));
+        assert_biteq!($f as f64, roundtrip!($f => f64));
+    }
+}
+
+macro_rules! assert_floats_bitne {
+    ($lhs:ident, $rhs:ident) => {
+        assert_ne!(f32::$lhs.to_bits(), f32::$rhs.to_bits());
+        assert_ne!(f64::$lhs.to_bits(), f64::$rhs.to_bits());
+    };
+    ($lhs:expr, $rhs:expr) => {
+        assert_ne!(f32::to_bits($lhs), f32::to_bits($rhs));
+        assert_ne!(f64::to_bits($lhs), f64::to_bits($rhs));
+    };
+}
+
+// We must preserve signs on all numbers. That includes zero.
+// -0 and 0 are == normally, so test bit equality.
+#[test]
+fn preserve_signed_zero() {
+    assert_floats_roundtrip!(-0.0);
+    assert_floats_roundtrip!(0.0);
+    assert_floats_bitne!(0.0, -0.0);
+}
+
+#[test]
+fn preserve_signed_infinity() {
+    assert_floats_roundtrip!(INFINITY);
+    assert_floats_roundtrip!(NEG_INFINITY);
+    assert_floats_bitne!(INFINITY, NEG_INFINITY);
+}
+
+#[test]
+fn infinity_to_str() {
+    assert!(match f32::INFINITY.to_string().to_lowercase().as_str() {
+        "+infinity" | "infinity" => true,
+        "+inf" | "inf" => true,
+        _ => false,
+    });
+    assert!(
+        match f64::INFINITY.to_string().to_lowercase().as_str() {
+            "+infinity" | "infinity" => true,
+            "+inf" | "inf" => true,
+            _ => false,
+        },
+        "Infinity must write to a string as some casing of inf or infinity, with an optional +."
+    );
+}
+
+#[test]
+fn neg_infinity_to_str() {
+    assert!(match f32::NEG_INFINITY.to_string().to_lowercase().as_str() {
+        "-infinity" | "-inf" => true,
+        _ => false,
+    });
+    assert!(
+        match f64::NEG_INFINITY.to_string().to_lowercase().as_str() {
+            "-infinity" | "-inf" => true,
+            _ => false,
+        },
+        "Negative Infinity must write to a string as some casing of -inf or -infinity"
+    )
+}
+
+#[test]
+fn nan_to_str() {
+    assert!(
+        match f32::NAN.to_string().to_lowercase().as_str() {
+            "nan" | "+nan" | "-nan" => true,
+            _ => false,
+        },
+        "NaNs must write to a string as some casing of nan."
+    )
+}
+
+// "+"?("inf"|"infinity") in any case => Infinity
+#[test]
+fn infinity_from_str() {
+    assert_biteq!(f32::INFINITY, f32::from_str("infinity").unwrap());
+    assert_biteq!(f32::INFINITY, f32::from_str("inf").unwrap());
+    assert_biteq!(f32::INFINITY, f32::from_str("+infinity").unwrap());
+    assert_biteq!(f32::INFINITY, f32::from_str("+inf").unwrap());
+    // yes! this means you are weLcOmE tO mY iNfInItElY tWiStEd MiNd
+    assert_biteq!(f32::INFINITY, f32::from_str("+iNfInItY").unwrap());
+}
+
+// "-inf"|"-infinity" in any case => Negative Infinity
+#[test]
+fn neg_infinity_from_str() {
+    assert_biteq!(f32::NEG_INFINITY, f32::from_str("-infinity").unwrap());
+    assert_biteq!(f32::NEG_INFINITY, f32::from_str("-inf").unwrap());
+    assert_biteq!(f32::NEG_INFINITY, f32::from_str("-INF").unwrap());
+    assert_biteq!(f32::NEG_INFINITY, f32::from_str("-INFinity").unwrap());
+}
+
+// ("+"|"-"")?"s"?"nan" in any case => qNaN
+#[test]
+fn qnan_from_str() {
+    assert!("nan".parse::<f32>().unwrap().is_nan());
+    assert!("-nan".parse::<f32>().unwrap().is_nan());
+    assert!("+nan".parse::<f32>().unwrap().is_nan());
+    assert!("+NAN".parse::<f32>().unwrap().is_nan());
+    assert!("-NaN".parse::<f32>().unwrap().is_nan());
+}
diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs
index e66a73ac128..bbb67667dfc 100644
--- a/library/core/tests/num/mod.rs
+++ b/library/core/tests/num/mod.rs
@@ -32,6 +32,7 @@ mod flt2dec;
 mod ops;
 mod wrapping;
 
+mod ieee754;
 mod nan;
 
 /// Adds the attribute to all items in the block.