about summary refs log tree commit diff
path: root/library/coretests/tests/num/dec2flt/mod.rs
blob: b8ca220847cfa73bc5b22cb2678b15866d02416d (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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#![allow(overflowing_literals)]

mod decimal;
mod decimal_seq;
mod float;
mod lemire;
mod parse;

// Take a float literal, turn it into a string in various ways (that are all trusted
// to be correct) and see if those strings are parsed back to the value of the literal.
// Requires a *polymorphic literal*, i.e., one that can serve as f64 as well as f32.
macro_rules! test_literal {
    ($x: expr) => {{
        #[cfg(target_has_reliable_f16)]
        let x16: f16 = $x;
        let x32: f32 = $x;
        let x64: f64 = $x;
        let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)];

        for input in inputs {
            assert_eq!(input.parse(), Ok(x64), "failed f64 {input}");
            assert_eq!(input.parse(), Ok(x32), "failed f32 {input}");
            #[cfg(target_has_reliable_f16)]
            assert_eq!(input.parse(), Ok(x16), "failed f16 {input}");

            let neg_input = format!("-{input}");
            assert_eq!(neg_input.parse(), Ok(-x64), "failed f64 {neg_input}");
            assert_eq!(neg_input.parse(), Ok(-x32), "failed f32 {neg_input}");
            #[cfg(target_has_reliable_f16)]
            assert_eq!(neg_input.parse(), Ok(-x16), "failed f16 {neg_input}");
        }
    }};
}

#[test]
fn ordinary() {
    test_literal!(1.0);
    test_literal!(3e-5);
    test_literal!(0.1);
    test_literal!(12345.);
    test_literal!(0.9999999);
    test_literal!(2.2250738585072014e-308);
}

#[test]
fn special_code_paths() {
    test_literal!(36893488147419103229.0); // 2^65 - 3, triggers half-to-even with even significand
    test_literal!(101e-33); // Triggers the tricky underflow case in AlgorithmM (for f32)
    test_literal!(1e23); // Triggers AlgorithmR
    test_literal!(2075e23); // Triggers another path through AlgorithmR
    test_literal!(8713e-23); // ... and yet another.
}

#[test]
fn large() {
    test_literal!(1e300);
    test_literal!(123456789.34567e250);
    test_literal!(943794359898089732078308743689303290943794359843568973207830874368930329.);
}

#[test]
fn subnormals() {
    test_literal!(5e-324);
    test_literal!(91e-324);
    test_literal!(1e-322);
    test_literal!(13245643e-320);
    test_literal!(2.22507385851e-308);
    test_literal!(2.1e-308);
    test_literal!(4.9406564584124654e-324);
}

#[test]
fn infinity() {
    test_literal!(1e400);
    test_literal!(1e309);
    test_literal!(2e308);
    test_literal!(1.7976931348624e308);
}

#[test]
fn zero() {
    test_literal!(0.0);
    test_literal!(1e-325);
    test_literal!(1e-326);
    test_literal!(1e-500);
}

#[test]
fn fast_path_correct() {
    // This number triggers the fast path and is handled incorrectly when compiling on
    // x86 without SSE2 (i.e., using the x87 FPU stack).
    test_literal!(1.448997445238699);
}

// FIXME(f16_f128): remove gates once tests work on all targets

#[test]
fn lonely_dot() {
    #[cfg(target_has_reliable_f16)]
    assert!(".".parse::<f16>().is_err());
    assert!(".".parse::<f32>().is_err());
    assert!(".".parse::<f64>().is_err());
}

#[test]
fn exponentiated_dot() {
    #[cfg(target_has_reliable_f16)]
    assert!(".e0".parse::<f16>().is_err());
    assert!(".e0".parse::<f32>().is_err());
    assert!(".e0".parse::<f64>().is_err());
}

#[test]
fn lonely_sign() {
    #[cfg(target_has_reliable_f16)]
    assert!("+".parse::<f16>().is_err());
    assert!("-".parse::<f32>().is_err());
    assert!("+".parse::<f64>().is_err());
}

#[test]
fn whitespace() {
    #[cfg(target_has_reliable_f16)]
    assert!("1.0 ".parse::<f16>().is_err());
    assert!(" 1.0".parse::<f32>().is_err());
    assert!("1.0 ".parse::<f64>().is_err());
}

#[test]
fn nan() {
    #[cfg(target_has_reliable_f16)]
    {
        assert!("NaN".parse::<f16>().unwrap().is_nan());
        assert!("-NaN".parse::<f16>().unwrap().is_nan());
    }

    assert!("NaN".parse::<f32>().unwrap().is_nan());
    assert!("-NaN".parse::<f32>().unwrap().is_nan());

    assert!("NaN".parse::<f64>().unwrap().is_nan());
    assert!("-NaN".parse::<f64>().unwrap().is_nan());
}

#[test]
fn inf() {
    #[cfg(target_has_reliable_f16)]
    {
        assert_eq!("inf".parse(), Ok(f16::INFINITY));
        assert_eq!("-inf".parse(), Ok(f16::NEG_INFINITY));
    }

    assert_eq!("inf".parse(), Ok(f32::INFINITY));
    assert_eq!("-inf".parse(), Ok(f32::NEG_INFINITY));

    assert_eq!("inf".parse(), Ok(f64::INFINITY));
    assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY));
}

#[test]
fn massive_exponent() {
    #[cfg(target_has_reliable_f16)]
    {
        let max = i16::MAX;
        assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY));
        assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f16));
        assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY));
    }

    let max = i32::MAX;
    assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY));
    assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f32));
    assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY));

    let max = i64::MAX;
    assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY));
    assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f64));
    assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY));
}