Skip to content

Commit 68d52c1

Browse files
tgross35speedy-lex
andcommitted
float: Add tests for f16 conversions to and from decimal
Extend the existing tests for `f32` and `f64` with versions that include `f16`'s new printing and parsing implementations. Co-authored-by: Speedy_Lex <[email protected]>
1 parent 4a6ba24 commit 68d52c1

File tree

12 files changed

+537
-78
lines changed

12 files changed

+537
-78
lines changed

compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ index 1e336bf..35e6f54 100644
1616
+++ b/coretests/tests/lib.rs
1717
@@ -2,5 +2,4 @@
1818
// tidy-alphabetical-start
19+
#![cfg_attr(not(bootstrap), feature(cfg_target_has_reliable_f16_f128))]
1920
-#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
2021
#![cfg_attr(test, feature(cfg_match))]
2122
#![feature(alloc_layout_extra)]
22-
#![feature(array_chunks)]
2323
diff --git a/coretests/tests/atomic.rs b/coretests/tests/atomic.rs
2424
index b735957..ea728b6 100644
2525
--- a/coretests/tests/atomic.rs

library/coretests/Cargo.toml

+9
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,12 @@ test = true
2626
[dev-dependencies]
2727
rand = { version = "0.9.0", default-features = false }
2828
rand_xorshift = { version = "0.4.0", default-features = false }
29+
30+
[lints.rust.unexpected_cfgs]
31+
level = "warn"
32+
check-cfg = [
33+
'cfg(bootstrap)',
34+
# Internal features aren't marked known config by default, we use these to
35+
# gate tests.
36+
'cfg(target_has_reliable_f16)',
37+
]

library/coretests/tests/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// tidy-alphabetical-start
2+
#![cfg_attr(not(bootstrap), feature(cfg_target_has_reliable_f16_f128))]
23
#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
34
#![cfg_attr(test, feature(cfg_match))]
45
#![feature(alloc_layout_extra)]
@@ -29,6 +30,7 @@
2930
#![feature(exact_size_is_empty)]
3031
#![feature(extend_one)]
3132
#![feature(extern_types)]
33+
#![feature(f16)]
3234
#![feature(float_minimum_maximum)]
3335
#![feature(flt2dec)]
3436
#![feature(fmt_internals)]

library/coretests/tests/num/dec2flt/decimal.rs

+15
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@ const FPATHS_F32: &[FPath<f32>] =
77
const FPATHS_F64: &[FPath<f64>] =
88
&[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))];
99

10+
// FIXME(f16_f128): enable on all targets once possible.
11+
#[test]
12+
#[cfg(not(bootstrap))]
13+
#[cfg(target_has_reliable_f16)]
14+
fn check_fast_path_f16() {
15+
const FPATHS_F16: &[FPath<f16>] =
16+
&[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))];
17+
for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F16.iter().copied() {
18+
let dec = Decimal { exponent, mantissa, negative, many_digits };
19+
let actual = dec.try_fast_path::<f16>();
20+
21+
assert_eq!(actual, expected);
22+
}
23+
}
24+
1025
#[test]
1126
fn check_fast_path_f32() {
1227
for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F32.iter().copied() {

library/coretests/tests/num/dec2flt/float.rs

+41
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
use core::num::dec2flt::float::RawFloat;
22

3+
// FIXME(f16_f128): enable on all targets once possible.
4+
#[test]
5+
#[cfg(not(bootstrap))]
6+
#[cfg(target_has_reliable_f16)]
7+
fn test_f16_integer_decode() {
8+
assert_eq!(3.14159265359f16.integer_decode(), (1608, -9, 1));
9+
assert_eq!((-8573.5918555f16).integer_decode(), (1072, 3, -1));
10+
assert_eq!(2f16.powf(14.0).integer_decode(), (1 << 10, 4, 1));
11+
assert_eq!(0f16.integer_decode(), (0, -25, 1));
12+
assert_eq!((-0f16).integer_decode(), (0, -25, -1));
13+
assert_eq!(f16::INFINITY.integer_decode(), (1 << 10, 6, 1));
14+
assert_eq!(f16::NEG_INFINITY.integer_decode(), (1 << 10, 6, -1));
15+
16+
// Ignore the "sign" (quiet / signalling flag) of NAN.
17+
// It can vary between runtime operations and LLVM folding.
18+
let (nan_m, nan_p, _nan_s) = f16::NAN.integer_decode();
19+
assert_eq!((nan_m, nan_p), (1536, 6));
20+
}
21+
322
#[test]
423
fn test_f32_integer_decode() {
524
assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1));
@@ -34,6 +53,28 @@ fn test_f64_integer_decode() {
3453

3554
/* Sanity checks of computed magic numbers */
3655

56+
// FIXME(f16_f128): enable on all targets once possible.
57+
#[test]
58+
#[cfg(not(bootstrap))]
59+
#[cfg(target_has_reliable_f16)]
60+
fn test_f16_consts() {
61+
assert_eq!(<f16 as RawFloat>::INFINITY, f16::INFINITY);
62+
assert_eq!(<f16 as RawFloat>::NEG_INFINITY, -f16::INFINITY);
63+
assert_eq!(<f16 as RawFloat>::NAN.to_bits(), f16::NAN.to_bits());
64+
assert_eq!(<f16 as RawFloat>::NEG_NAN.to_bits(), (-f16::NAN).to_bits());
65+
assert_eq!(<f16 as RawFloat>::SIG_BITS, 10);
66+
assert_eq!(<f16 as RawFloat>::MIN_EXPONENT_ROUND_TO_EVEN, -22);
67+
assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_ROUND_TO_EVEN, 5);
68+
assert_eq!(<f16 as RawFloat>::MIN_EXPONENT_FAST_PATH, -4);
69+
assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_FAST_PATH, 4);
70+
assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_DISGUISED_FAST_PATH, 7);
71+
assert_eq!(<f16 as RawFloat>::EXP_MIN, -14);
72+
assert_eq!(<f16 as RawFloat>::EXP_SAT, 0x1f);
73+
assert_eq!(<f16 as RawFloat>::SMALLEST_POWER_OF_TEN, -27);
74+
assert_eq!(<f16 as RawFloat>::LARGEST_POWER_OF_TEN, 4);
75+
assert_eq!(<f16 as RawFloat>::MAX_MANTISSA_FAST_PATH, 2048);
76+
}
77+
3778
#[test]
3879
fn test_f32_consts() {
3980
assert_eq!(<f32 as RawFloat>::INFINITY, f32::INFINITY);
+104-31
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
use core::num::dec2flt::float::RawFloat;
22
use core::num::dec2flt::lemire::compute_float;
33

4+
#[cfg(not(bootstrap))]
5+
#[cfg(target_has_reliable_f16)]
6+
fn compute_float16(q: i64, w: u64) -> (i32, u64) {
7+
let fp = compute_float::<f16>(q, w);
8+
(fp.p_biased, fp.m)
9+
}
10+
411
fn compute_float32(q: i64, w: u64) -> (i32, u64) {
512
let fp = compute_float::<f32>(q, w);
613
(fp.p_biased, fp.m)
@@ -11,23 +18,74 @@ fn compute_float64(q: i64, w: u64) -> (i32, u64) {
1118
(fp.p_biased, fp.m)
1219
}
1320

21+
// FIXME(f16_f128): enable on all targets once possible.
22+
#[test]
23+
#[cfg(not(bootstrap))]
24+
#[cfg(target_has_reliable_f16)]
25+
fn compute_float_f16_rounding() {
26+
// The maximum integer that cna be converted to a `f16` without lost precision.
27+
let val = 1 << 11;
28+
let scale = 10_u64.pow(10);
29+
30+
// These test near-halfway cases for half-precision floats.
31+
assert_eq!(compute_float16(0, val), (26, 0));
32+
assert_eq!(compute_float16(0, val + 1), (26, 0));
33+
assert_eq!(compute_float16(0, val + 2), (26, 1));
34+
assert_eq!(compute_float16(0, val + 3), (26, 2));
35+
assert_eq!(compute_float16(0, val + 4), (26, 2));
36+
37+
// For the next power up, the two nearest representable numbers are twice as far apart.
38+
let val2 = 1 << 12;
39+
assert_eq!(compute_float16(0, val2), (27, 0));
40+
assert_eq!(compute_float16(0, val2 + 2), (27, 0));
41+
assert_eq!(compute_float16(0, val2 + 4), (27, 1));
42+
assert_eq!(compute_float16(0, val2 + 6), (27, 2));
43+
assert_eq!(compute_float16(0, val2 + 8), (27, 2));
44+
45+
// These are examples of the above tests, with digits from the exponent shifted
46+
// to the mantissa.
47+
assert_eq!(compute_float16(-10, val * scale), (26, 0));
48+
assert_eq!(compute_float16(-10, (val + 1) * scale), (26, 0));
49+
assert_eq!(compute_float16(-10, (val + 2) * scale), (26, 1));
50+
// Let's check the lines to see if anything is different in table...
51+
assert_eq!(compute_float16(-10, (val + 3) * scale), (26, 2));
52+
assert_eq!(compute_float16(-10, (val + 4) * scale), (26, 2));
53+
54+
// Check the rounding point between infinity and the next representable number down
55+
assert_eq!(compute_float16(4, 6), (f16::INFINITE_POWER - 1, 851));
56+
assert_eq!(compute_float16(4, 7), (f16::INFINITE_POWER, 0)); // infinity
57+
assert_eq!(compute_float16(2, 655), (f16::INFINITE_POWER - 1, 1023));
58+
}
59+
1460
#[test]
1561
fn compute_float_f32_rounding() {
62+
// the maximum integer that cna be converted to a `f32` without lost precision.
63+
let val = 1 << 24;
64+
let scale = 10_u64.pow(10);
65+
1666
// These test near-halfway cases for single-precision floats.
17-
assert_eq!(compute_float32(0, 16777216), (151, 0));
18-
assert_eq!(compute_float32(0, 16777217), (151, 0));
19-
assert_eq!(compute_float32(0, 16777218), (151, 1));
20-
assert_eq!(compute_float32(0, 16777219), (151, 2));
21-
assert_eq!(compute_float32(0, 16777220), (151, 2));
22-
23-
// These are examples of the above tests, with
24-
// digits from the exponent shifted to the mantissa.
25-
assert_eq!(compute_float32(-10, 167772160000000000), (151, 0));
26-
assert_eq!(compute_float32(-10, 167772170000000000), (151, 0));
27-
assert_eq!(compute_float32(-10, 167772180000000000), (151, 1));
67+
assert_eq!(compute_float32(0, val), (151, 0));
68+
assert_eq!(compute_float32(0, val + 1), (151, 0));
69+
assert_eq!(compute_float32(0, val + 2), (151, 1));
70+
assert_eq!(compute_float32(0, val + 3), (151, 2));
71+
assert_eq!(compute_float32(0, val + 4), (151, 2));
72+
73+
// For the next power up, the two nearest representable numbers are twice as far apart.
74+
let val2 = 1 << 25;
75+
assert_eq!(compute_float32(0, val2), (152, 0));
76+
assert_eq!(compute_float32(0, val2 + 2), (152, 0));
77+
assert_eq!(compute_float32(0, val2 + 4), (152, 1));
78+
assert_eq!(compute_float32(0, val2 + 6), (152, 2));
79+
assert_eq!(compute_float32(0, val2 + 8), (152, 2));
80+
81+
// These are examples of the above tests, with digits from the exponent shifted
82+
// to the mantissa.
83+
assert_eq!(compute_float32(-10, val * scale), (151, 0));
84+
assert_eq!(compute_float32(-10, (val + 1) * scale), (151, 0));
85+
assert_eq!(compute_float32(-10, (val + 2) * scale), (151, 1));
2886
// Let's check the lines to see if anything is different in table...
29-
assert_eq!(compute_float32(-10, 167772190000000000), (151, 2));
30-
assert_eq!(compute_float32(-10, 167772200000000000), (151, 2));
87+
assert_eq!(compute_float32(-10, (val + 3) * scale), (151, 2));
88+
assert_eq!(compute_float32(-10, (val + 4) * scale), (151, 2));
3189

3290
// Check the rounding point between infinity and the next representable number down
3391
assert_eq!(compute_float32(38, 3), (f32::INFINITE_POWER - 1, 6402534));
@@ -37,23 +95,38 @@ fn compute_float_f32_rounding() {
3795

3896
#[test]
3997
fn compute_float_f64_rounding() {
98+
// The maximum integer that cna be converted to a `f64` without lost precision.
99+
let val = 1 << 53;
100+
let scale = 1000;
101+
40102
// These test near-halfway cases for double-precision floats.
41-
assert_eq!(compute_float64(0, 9007199254740992), (1076, 0));
42-
assert_eq!(compute_float64(0, 9007199254740993), (1076, 0));
43-
assert_eq!(compute_float64(0, 9007199254740994), (1076, 1));
44-
assert_eq!(compute_float64(0, 9007199254740995), (1076, 2));
45-
assert_eq!(compute_float64(0, 9007199254740996), (1076, 2));
46-
assert_eq!(compute_float64(0, 18014398509481984), (1077, 0));
47-
assert_eq!(compute_float64(0, 18014398509481986), (1077, 0));
48-
assert_eq!(compute_float64(0, 18014398509481988), (1077, 1));
49-
assert_eq!(compute_float64(0, 18014398509481990), (1077, 2));
50-
assert_eq!(compute_float64(0, 18014398509481992), (1077, 2));
51-
52-
// These are examples of the above tests, with
53-
// digits from the exponent shifted to the mantissa.
54-
assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0));
55-
assert_eq!(compute_float64(-3, 9007199254740993000), (1076, 0));
56-
assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1));
57-
assert_eq!(compute_float64(-3, 9007199254740995000), (1076, 2));
58-
assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2));
103+
assert_eq!(compute_float64(0, val), (1076, 0));
104+
assert_eq!(compute_float64(0, val + 1), (1076, 0));
105+
assert_eq!(compute_float64(0, val + 2), (1076, 1));
106+
assert_eq!(compute_float64(0, val + 3), (1076, 2));
107+
assert_eq!(compute_float64(0, val + 4), (1076, 2));
108+
109+
// For the next power up, the two nearest representable numbers are twice as far apart.
110+
let val2 = 1 << 54;
111+
assert_eq!(compute_float64(0, val2), (1077, 0));
112+
assert_eq!(compute_float64(0, val2 + 2), (1077, 0));
113+
assert_eq!(compute_float64(0, val2 + 4), (1077, 1));
114+
assert_eq!(compute_float64(0, val2 + 6), (1077, 2));
115+
assert_eq!(compute_float64(0, val2 + 8), (1077, 2));
116+
117+
// These are examples of the above tests, with digits from the exponent shifted
118+
// to the mantissa.
119+
assert_eq!(compute_float64(-3, val * scale), (1076, 0));
120+
assert_eq!(compute_float64(-3, (val + 1) * scale), (1076, 0));
121+
assert_eq!(compute_float64(-3, (val + 2) * scale), (1076, 1));
122+
assert_eq!(compute_float64(-3, (val + 3) * scale), (1076, 2));
123+
assert_eq!(compute_float64(-3, (val + 4) * scale), (1076, 2));
124+
125+
// Check the rounding point between infinity and the next representable number down
126+
assert_eq!(compute_float64(308, 1), (f64::INFINITE_POWER - 1, 506821272651936));
127+
assert_eq!(compute_float64(308, 2), (f64::INFINITE_POWER, 0)); // infinity
128+
assert_eq!(
129+
compute_float64(292, 17976931348623157),
130+
(f64::INFINITE_POWER - 1, 4503599627370495)
131+
);
59132
}

library/coretests/tests/num/dec2flt/mod.rs

+60-9
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,20 @@ mod parse;
1111
// Requires a *polymorphic literal*, i.e., one that can serve as f64 as well as f32.
1212
macro_rules! test_literal {
1313
($x: expr) => {{
14+
let x16: f16 = $x;
1415
let x32: f32 = $x;
1516
let x64: f64 = $x;
1617
let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)];
18+
1719
for input in inputs {
18-
assert_eq!(input.parse(), Ok(x64));
19-
assert_eq!(input.parse(), Ok(x32));
20+
assert_eq!(input.parse(), Ok(x64), "failed f64 {input}");
21+
assert_eq!(input.parse(), Ok(x32), "failed f32 {input}");
22+
assert_eq!(input.parse(), Ok(x16), "failed f16 {input}");
23+
2024
let neg_input = format!("-{input}");
21-
assert_eq!(neg_input.parse(), Ok(-x64));
22-
assert_eq!(neg_input.parse(), Ok(-x32));
25+
assert_eq!(neg_input.parse(), Ok(-x64), "failed f64 {neg_input}");
26+
assert_eq!(neg_input.parse(), Ok(-x32), "failed f32 {neg_input}");
27+
assert_eq!(neg_input.parse(), Ok(-x16), "failed f16 {neg_input}");
2328
}
2429
}};
2530
}
@@ -84,48 +89,94 @@ fn fast_path_correct() {
8489
test_literal!(1.448997445238699);
8590
}
8691

92+
// FIXME(f16_f128): remove gates once tests work on all targets
93+
8794
#[test]
8895
fn lonely_dot() {
96+
#[cfg(not(bootstrap))]
97+
#[cfg(target_has_reliable_f16)]
98+
assert!(".".parse::<f16>().is_err());
8999
assert!(".".parse::<f32>().is_err());
90100
assert!(".".parse::<f64>().is_err());
91101
}
92102

93103
#[test]
94104
fn exponentiated_dot() {
105+
#[cfg(not(bootstrap))]
106+
#[cfg(target_has_reliable_f16)]
107+
assert!(".e0".parse::<f16>().is_err());
95108
assert!(".e0".parse::<f32>().is_err());
96109
assert!(".e0".parse::<f64>().is_err());
97110
}
98111

99112
#[test]
100113
fn lonely_sign() {
101-
assert!("+".parse::<f32>().is_err());
102-
assert!("-".parse::<f64>().is_err());
114+
#[cfg(not(bootstrap))]
115+
#[cfg(target_has_reliable_f16)]
116+
assert!("+".parse::<f16>().is_err());
117+
assert!("-".parse::<f32>().is_err());
118+
assert!("+".parse::<f64>().is_err());
103119
}
104120

105121
#[test]
106122
fn whitespace() {
123+
#[cfg(not(bootstrap))]
124+
#[cfg(target_has_reliable_f16)]
125+
assert!("1.0 ".parse::<f16>().is_err());
107126
assert!(" 1.0".parse::<f32>().is_err());
108127
assert!("1.0 ".parse::<f64>().is_err());
109128
}
110129

111130
#[test]
112131
fn nan() {
132+
#[cfg(not(bootstrap))]
133+
#[cfg(target_has_reliable_f16)]
134+
{
135+
assert!("NaN".parse::<f16>().unwrap().is_nan());
136+
assert!("-NaN".parse::<f16>().unwrap().is_nan());
137+
}
138+
113139
assert!("NaN".parse::<f32>().unwrap().is_nan());
140+
assert!("-NaN".parse::<f32>().unwrap().is_nan());
141+
114142
assert!("NaN".parse::<f64>().unwrap().is_nan());
143+
assert!("-NaN".parse::<f64>().unwrap().is_nan());
115144
}
116145

117146
#[test]
118147
fn inf() {
119-
assert_eq!("inf".parse(), Ok(f64::INFINITY));
120-
assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY));
148+
#[cfg(not(bootstrap))]
149+
#[cfg(target_has_reliable_f16)]
150+
{
151+
assert_eq!("inf".parse(), Ok(f16::INFINITY));
152+
assert_eq!("-inf".parse(), Ok(f16::NEG_INFINITY));
153+
}
154+
121155
assert_eq!("inf".parse(), Ok(f32::INFINITY));
122156
assert_eq!("-inf".parse(), Ok(f32::NEG_INFINITY));
157+
158+
assert_eq!("inf".parse(), Ok(f64::INFINITY));
159+
assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY));
123160
}
124161

125162
#[test]
126163
fn massive_exponent() {
164+
#[cfg(not(bootstrap))]
165+
#[cfg(target_has_reliable_f16)]
166+
{
167+
let max = i16::MAX;
168+
assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY));
169+
assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f16));
170+
assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY));
171+
}
172+
173+
let max = i32::MAX;
174+
assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY));
175+
assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f32));
176+
assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY));
177+
127178
let max = i64::MAX;
128179
assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY));
129-
assert_eq!(format!("1e-{max}000").parse(), Ok(0.0));
180+
assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f64));
130181
assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY));
131182
}

0 commit comments

Comments
 (0)