summaryrefslogtreecommitdiffstats
path: root/library/core/tests/num/ieee754.rs
blob: f6e5dfc98c793b160b778c050437415fa17dd41e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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());
}