Skip to content

Commit 977d841

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 <alex.ciocildau@gmail.com>
1 parent 6fc60b8 commit 977d841

9 files changed

Lines changed: 501 additions & 77 deletions

File tree

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ const FPATHS_F32: &[FPath<f32>] =
77
constFPATHS_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(target_has_reliable_f16)]
13+
fncheck_fast_path_f16(){
14+
constFPATHS_F16:&[FPath<f16>] =
15+
&[((0,0,false,false),Some(0.0)),((0,0,false,false),Some(0.0))];
16+
for((exponent, mantissa, negative, many_digits), expected)inFPATHS_F16.iter().copied(){
17+
let dec = Decimal{ exponent, mantissa, negative, many_digits };
18+
let actual = dec.try_fast_path::<f16>();
19+
20+
assert_eq!(actual, expected);
21+
}
22+
}
23+
1024
#[test]
1125
fncheck_fast_path_f32(){
1226
for((exponent, mantissa, negative, many_digits), expected)inFPATHS_F32.iter().copied(){

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

Lines changed: 40 additions & 0 deletions
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(target_has_reliable_f16)]
6+
fntest_f16_integer_decode(){
7+
assert_eq!(3.14159265359f16.integer_decode(),(1608, -9,1));
8+
assert_eq!((-8573.5918555f16).integer_decode(),(1072,3, -1));
9+
#[cfg(not(miri))]// miri doesn't have powf16
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
fntest_f32_integer_decode(){
524
assert_eq!(3.14159265359f32.integer_decode(),(13176795, -22,1));
@@ -34,6 +53,27 @@ 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(target_has_reliable_f16)]
59+
fntest_f16_consts(){
60+
assert_eq!(<f16 asRawFloat>::INFINITY, f16::INFINITY);
61+
assert_eq!(<f16 asRawFloat>::NEG_INFINITY, -f16::INFINITY);
62+
assert_eq!(<f16 asRawFloat>::NAN.to_bits(), f16::NAN.to_bits());
63+
assert_eq!(<f16 asRawFloat>::NEG_NAN.to_bits(),(-f16::NAN).to_bits());
64+
assert_eq!(<f16 asRawFloat>::SIG_BITS,10);
65+
assert_eq!(<f16 asRawFloat>::MIN_EXPONENT_ROUND_TO_EVEN, -22);
66+
assert_eq!(<f16 asRawFloat>::MAX_EXPONENT_ROUND_TO_EVEN,5);
67+
assert_eq!(<f16 asRawFloat>::MIN_EXPONENT_FAST_PATH, -4);
68+
assert_eq!(<f16 asRawFloat>::MAX_EXPONENT_FAST_PATH,4);
69+
assert_eq!(<f16 asRawFloat>::MAX_EXPONENT_DISGUISED_FAST_PATH,7);
70+
assert_eq!(<f16 asRawFloat>::EXP_MIN, -14);
71+
assert_eq!(<f16 asRawFloat>::EXP_SAT,0x1f);
72+
assert_eq!(<f16 asRawFloat>::SMALLEST_POWER_OF_TEN, -27);
73+
assert_eq!(<f16 asRawFloat>::LARGEST_POWER_OF_TEN,4);
74+
assert_eq!(<f16 asRawFloat>::MAX_MANTISSA_FAST_PATH,2048);
75+
}
76+
3777
#[test]
3878
fntest_f32_consts(){
3979
assert_eq!(<f32asRawFloat>::INFINITY,f32::INFINITY);
Lines changed: 102 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
use core::num::dec2flt::float::RawFloat;
22
use core::num::dec2flt::lemire::compute_float;
33

4+
#[cfg(target_has_reliable_f16)]
5+
fncompute_float16(q:i64,w:u64) -> (i32,u64){
6+
let fp = compute_float::<f16>(q, w);
7+
(fp.p_biased, fp.m)
8+
}
9+
410
fncompute_float32(q:i64,w:u64) -> (i32,u64){
511
let fp = compute_float::<f32>(q, w);
612
(fp.p_biased, fp.m)
@@ -11,23 +17,73 @@ fn compute_float64(q: i64, w: u64) -> (i32, u64) {
1117
(fp.p_biased, fp.m)
1218
}
1319

20+
// FIXME(f16_f128): enable on all targets once possible.
21+
#[test]
22+
#[cfg(target_has_reliable_f16)]
23+
fncompute_float_f16_rounding(){
24+
// The maximum integer that cna be converted to a `f16` without lost precision.
25+
let val = 1 << 11;
26+
let scale = 10_u64.pow(10);
27+
28+
// These test near-halfway cases for half-precision floats.
29+
assert_eq!(compute_float16(0, val),(26,0));
30+
assert_eq!(compute_float16(0, val + 1),(26,0));
31+
assert_eq!(compute_float16(0, val + 2),(26,1));
32+
assert_eq!(compute_float16(0, val + 3),(26,2));
33+
assert_eq!(compute_float16(0, val + 4),(26,2));
34+
35+
// For the next power up, the two nearest representable numbers are twice as far apart.
36+
let val2 = 1 << 12;
37+
assert_eq!(compute_float16(0, val2),(27,0));
38+
assert_eq!(compute_float16(0, val2 + 2),(27,0));
39+
assert_eq!(compute_float16(0, val2 + 4),(27,1));
40+
assert_eq!(compute_float16(0, val2 + 6),(27,2));
41+
assert_eq!(compute_float16(0, val2 + 8),(27,2));
42+
43+
// These are examples of the above tests, with digits from the exponent shifted
44+
// to the mantissa.
45+
assert_eq!(compute_float16(-10, val * scale),(26,0));
46+
assert_eq!(compute_float16(-10,(val + 1)* scale),(26,0));
47+
assert_eq!(compute_float16(-10,(val + 2)* scale),(26,1));
48+
// Let's check the lines to see if anything is different in table...
49+
assert_eq!(compute_float16(-10,(val + 3)* scale),(26,2));
50+
assert_eq!(compute_float16(-10,(val + 4)* scale),(26,2));
51+
52+
// Check the rounding point between infinity and the next representable number down
53+
assert_eq!(compute_float16(4,6),(f16::INFINITE_POWER - 1,851));
54+
assert_eq!(compute_float16(4,7),(f16::INFINITE_POWER,0));// infinity
55+
assert_eq!(compute_float16(2,655),(f16::INFINITE_POWER - 1,1023));
56+
}
57+
1458
#[test]
1559
fncompute_float_f32_rounding(){
60+
// the maximum integer that cna be converted to a `f32` without lost precision.
61+
let val = 1 << 24;
62+
let scale = 10_u64.pow(10);
63+
1664
// 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));
65+
assert_eq!(compute_float32(0, val),(151,0));
66+
assert_eq!(compute_float32(0, val + 1),(151,0));
67+
assert_eq!(compute_float32(0, val + 2),(151,1));
68+
assert_eq!(compute_float32(0, val + 3),(151,2));
69+
assert_eq!(compute_float32(0, val + 4),(151,2));
70+
71+
// For the next power up, the two nearest representable numbers are twice as far apart.
72+
let val2 = 1 << 25;
73+
assert_eq!(compute_float32(0, val2),(152,0));
74+
assert_eq!(compute_float32(0, val2 + 2),(152,0));
75+
assert_eq!(compute_float32(0, val2 + 4),(152,1));
76+
assert_eq!(compute_float32(0, val2 + 6),(152,2));
77+
assert_eq!(compute_float32(0, val2 + 8),(152,2));
78+
79+
// These are examples of the above tests, with digits from the exponent shifted
80+
// to the mantissa.
81+
assert_eq!(compute_float32(-10, val * scale),(151,0));
82+
assert_eq!(compute_float32(-10,(val + 1)* scale),(151,0));
83+
assert_eq!(compute_float32(-10,(val + 2)* scale),(151,1));
2884
// 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));
85+
assert_eq!(compute_float32(-10,(val + 3)* scale),(151,2));
86+
assert_eq!(compute_float32(-10,(val + 4)* scale),(151,2));
3187

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

3894
#[test]
3995
fncompute_float_f64_rounding(){
96+
// The maximum integer that cna be converted to a `f64` without lost precision.
97+
let val = 1 << 53;
98+
let scale = 1000;
99+
40100
// 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));
101+
assert_eq!(compute_float64(0, val),(1076,0));
102+
assert_eq!(compute_float64(0, val + 1),(1076,0));
103+
assert_eq!(compute_float64(0, val + 2),(1076,1));
104+
assert_eq!(compute_float64(0, val + 3),(1076,2));
105+
assert_eq!(compute_float64(0, val + 4),(1076,2));
106+
107+
// For the next power up, the two nearest representable numbers are twice as far apart.
108+
let val2 = 1 << 54;
109+
assert_eq!(compute_float64(0, val2),(1077,0));
110+
assert_eq!(compute_float64(0, val2 + 2),(1077,0));
111+
assert_eq!(compute_float64(0, val2 + 4),(1077,1));
112+
assert_eq!(compute_float64(0, val2 + 6),(1077,2));
113+
assert_eq!(compute_float64(0, val2 + 8),(1077,2));
114+
115+
// These are examples of the above tests, with digits from the exponent shifted
116+
// to the mantissa.
117+
assert_eq!(compute_float64(-3, val * scale),(1076,0));
118+
assert_eq!(compute_float64(-3,(val + 1)* scale),(1076,0));
119+
assert_eq!(compute_float64(-3,(val + 2)* scale),(1076,1));
120+
assert_eq!(compute_float64(-3,(val + 3)* scale),(1076,2));
121+
assert_eq!(compute_float64(-3,(val + 4)* scale),(1076,2));
122+
123+
// Check the rounding point between infinity and the next representable number down
124+
assert_eq!(compute_float64(308,1),(f64::INFINITE_POWER - 1,506821272651936));
125+
assert_eq!(compute_float64(308,2),(f64::INFINITE_POWER,0));// infinity
126+
assert_eq!(
127+
compute_float64(292,17976931348623157),
128+
(f64::INFINITE_POWER - 1,4503599627370495)
129+
);
59130
}

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

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,23 @@ 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+
#[cfg(target_has_reliable_f16)]
15+
let x16: f16 = $x;
1416
let x32:f32 = $x;
1517
let x64:f64 = $x;
1618
let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)];
19+
1720
for input in inputs {
18-
assert_eq!(input.parse(),Ok(x64));
19-
assert_eq!(input.parse(),Ok(x32));
21+
assert_eq!(input.parse(),Ok(x64),"failed f64 {input}");
22+
assert_eq!(input.parse(),Ok(x32),"failed f32 {input}");
23+
#[cfg(target_has_reliable_f16)]
24+
assert_eq!(input.parse(),Ok(x16),"failed f16 {input}");
25+
2026
let neg_input = format!("-{input}");
21-
assert_eq!(neg_input.parse(),Ok(-x64));
22-
assert_eq!(neg_input.parse(),Ok(-x32));
27+
assert_eq!(neg_input.parse(),Ok(-x64),"failed f64 {neg_input}");
28+
assert_eq!(neg_input.parse(),Ok(-x32),"failed f32 {neg_input}");
29+
#[cfg(target_has_reliable_f16)]
30+
assert_eq!(neg_input.parse(),Ok(-x16),"failed f16 {neg_input}");
2331
}
2432
}};
2533
}
@@ -84,48 +92,87 @@ fn fast_path_correct() {
8492
test_literal!(1.448997445238699);
8593
}
8694

95+
// FIXME(f16_f128): remove gates once tests work on all targets
96+
8797
#[test]
8898
fnlonely_dot(){
99+
#[cfg(target_has_reliable_f16)]
100+
assert!(".".parse::<f16>().is_err());
89101
assert!(".".parse::<f32>().is_err());
90102
assert!(".".parse::<f64>().is_err());
91103
}
92104

93105
#[test]
94106
fnexponentiated_dot(){
107+
#[cfg(target_has_reliable_f16)]
108+
assert!(".e0".parse::<f16>().is_err());
95109
assert!(".e0".parse::<f32>().is_err());
96110
assert!(".e0".parse::<f64>().is_err());
97111
}
98112

99113
#[test]
100114
fnlonely_sign(){
101-
assert!("+".parse::<f32>().is_err());
102-
assert!("-".parse::<f64>().is_err());
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
fnwhitespace(){
123+
#[cfg(target_has_reliable_f16)]
124+
assert!("1.0 ".parse::<f16>().is_err());
107125
assert!(" 1.0".parse::<f32>().is_err());
108126
assert!("1.0 ".parse::<f64>().is_err());
109127
}
110128

111129
#[test]
112130
fnnan(){
131+
#[cfg(target_has_reliable_f16)]
132+
{
133+
assert!("NaN".parse::<f16>().unwrap().is_nan());
134+
assert!("-NaN".parse::<f16>().unwrap().is_nan());
135+
}
136+
113137
assert!("NaN".parse::<f32>().unwrap().is_nan());
138+
assert!("-NaN".parse::<f32>().unwrap().is_nan());
139+
114140
assert!("NaN".parse::<f64>().unwrap().is_nan());
141+
assert!("-NaN".parse::<f64>().unwrap().is_nan());
115142
}
116143

117144
#[test]
118145
fninf(){
119-
assert_eq!("inf".parse(),Ok(f64::INFINITY));
120-
assert_eq!("-inf".parse(),Ok(f64::NEG_INFINITY));
146+
#[cfg(target_has_reliable_f16)]
147+
{
148+
assert_eq!("inf".parse(),Ok(f16::INFINITY));
149+
assert_eq!("-inf".parse(),Ok(f16::NEG_INFINITY));
150+
}
151+
121152
assert_eq!("inf".parse(),Ok(f32::INFINITY));
122153
assert_eq!("-inf".parse(),Ok(f32::NEG_INFINITY));
154+
155+
assert_eq!("inf".parse(),Ok(f64::INFINITY));
156+
assert_eq!("-inf".parse(),Ok(f64::NEG_INFINITY));
123157
}
124158

125159
#[test]
126160
fnmassive_exponent(){
161+
#[cfg(target_has_reliable_f16)]
162+
{
163+
let max = i16::MAX;
164+
assert_eq!(format!("1e{max}000").parse(),Ok(f16::INFINITY));
165+
assert_eq!(format!("1e-{max}000").parse(),Ok(0.0f16));
166+
assert_eq!(format!("1e{max}000").parse(),Ok(f16::INFINITY));
167+
}
168+
169+
let max = i32::MAX;
170+
assert_eq!(format!("1e{max}000").parse(),Ok(f32::INFINITY));
171+
assert_eq!(format!("1e-{max}000").parse(),Ok(0.0f32));
172+
assert_eq!(format!("1e{max}000").parse(),Ok(f32::INFINITY));
173+
127174
let max = i64::MAX;
128175
assert_eq!(format!("1e{max}000").parse(),Ok(f64::INFINITY));
129-
assert_eq!(format!("1e-{max}000").parse(),Ok(0.0));
176+
assert_eq!(format!("1e-{max}000").parse(),Ok(0.0f64));
130177
assert_eq!(format!("1e{max}000").parse(),Ok(f64::INFINITY));
131178
}

‎library/coretests/tests/num/dec2flt/parse.rs‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,41 @@ fn new_dec(e: i64, m: u64) -> Decimal {
1010
fnmissing_pieces(){
1111
let permutations = &[".e","1e","e4","e",".12e","321.e","32.12e+","12.32e-"];
1212
for&s in permutations {
13+
#[cfg(target_has_reliable_f16)]
14+
assert_eq!(dec2flt::<f16>(s),Err(pfe_invalid()));
15+
assert_eq!(dec2flt::<f32>(s),Err(pfe_invalid()));
1316
assert_eq!(dec2flt::<f64>(s),Err(pfe_invalid()));
1417
}
1518
}
1619

1720
#[test]
1821
fninvalid_chars(){
1922
let invalid = "r,?<j";
20-
let error = Err(pfe_invalid());
2123
let valid_strings = &["123","666.",".1","5e1","7e-3","0.0e+1"];
24+
2225
for c in invalid.chars(){
2326
for s in valid_strings {
2427
for i in0..s.len(){
2528
letmut input = String::new();
2629
input.push_str(s);
2730
input.insert(i, c);
28-
assert!(dec2flt::<f64>(&input) == error,"did not reject invalid {:?}", input);
31+
32+
#[cfg(target_has_reliable_f16)]
33+
assert_eq!(
34+
dec2flt::<f16>(&input),
35+
Err(pfe_invalid()),
36+
"f16 did not reject invalid {input:?}",
37+
);
38+
assert_eq!(
39+
dec2flt::<f32>(&input),
40+
Err(pfe_invalid()),
41+
"f32 did not reject invalid {input:?}",
42+
);
43+
assert_eq!(
44+
dec2flt::<f64>(&input),
45+
Err(pfe_invalid()),
46+
"f64 did not reject invalid {input:?}",
47+
);
2948
}
3049
}
3150
}

0 commit comments

Comments
 (0)