Skip to content

Commit c04e284

Browse files
committed
libm: Fix _status status outputs on null-mantissa inputs
Values like 0.5 that have an exponent but not a mantissa are currently being reported as OK rather than INEXACT for ceil, floor, and trunc. Fix this and clean up the test cases.
1 parent 9089e95 commit c04e284

File tree

3 files changed

+188
-234
lines changed

3 files changed

+188
-234
lines changed

libm/src/math/generic/ceil.rs

Lines changed: 61 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub fn ceil_status<F: Float>(x: F) -> FpResult<F> {
4646
F::from_bits(ix)
4747
} else {
4848
// |x| < 1.0, raise an inexact exception since truncation will happen (unless x == 0).
49-
if ix & F::SIG_MASK == F::Int::ZERO {
49+
if ix & !F::SIGN_MASK == F::Int::ZERO {
5050
status = Status::OK;
5151
} else {
5252
status = Status::INEXACT;
@@ -72,103 +72,83 @@ mod tests {
7272
use super::*;
7373
use crate::support::Hexf;
7474

75-
/// Test against https://en.cppreference.com/w/cpp/numeric/math/ceil
76-
fn spec_test<F: Float>(cases: &[(F, F, Status)]) {
77-
let roundtrip = [
78-
F::ZERO,
79-
F::ONE,
80-
F::NEG_ONE,
81-
F::NEG_ZERO,
82-
F::INFINITY,
83-
F::NEG_INFINITY,
84-
];
85-
86-
for x in roundtrip {
87-
let FpResult { val, status } = ceil_status(x);
88-
assert_biteq!(val, x, "{}", Hexf(x));
89-
assert_eq!(status, Status::OK, "{}", Hexf(x));
90-
}
75+
macro_rules! cases {
76+
($f:ty) => {
77+
[
78+
// roundtrip
79+
(0.0, 0.0, Status::OK),
80+
(-0.0, -0.0, Status::OK),
81+
(1.0, 1.0, Status::OK),
82+
(-1.0, -1.0, Status::OK),
83+
(<$f>::INFINITY, <$f>::INFINITY, Status::OK),
84+
(<$f>::NEG_INFINITY, <$f>::NEG_INFINITY, Status::OK),
85+
// with rounding
86+
(0.1, 1.0, Status::INEXACT),
87+
(-0.1, -0.0, Status::INEXACT),
88+
(0.5, 1.0, Status::INEXACT),
89+
(-0.5, -0.0, Status::INEXACT),
90+
(0.9, 1.0, Status::INEXACT),
91+
(-0.9, -0.0, Status::INEXACT),
92+
(1.1, 2.0, Status::INEXACT),
93+
(-1.1, -1.0, Status::INEXACT),
94+
(1.5, 2.0, Status::INEXACT),
95+
(-1.5, -1.0, Status::INEXACT),
96+
(1.9, 2.0, Status::INEXACT),
97+
(-1.9, -1.0, Status::INEXACT),
98+
]
99+
};
100+
}
91101

92-
for &(x, res, res_stat) in cases {
102+
#[track_caller]
103+
fn check<F: Float>(cases: &[(F, F, Status)]) {
104+
for &(x, exp_res, exp_stat) in cases {
93105
let FpResult { val, status } = ceil_status(x);
94-
assert_biteq!(val, res, "{}", Hexf(x));
95-
assert_eq!(status, res_stat, "{}", Hexf(x));
106+
assert_biteq!(val, exp_res, "{x:?} {}", Hexf(x));
107+
assert_eq!(
108+
status,
109+
exp_stat,
110+
"{x:?} {} -> {exp_res:?} {}",
111+
Hexf(x),
112+
Hexf(exp_res)
113+
);
96114
}
97115
}
98116

99-
/* Skipping f16 / f128 "sanity_check"s due to rejected literal lexing at MSRV */
100-
101117
#[test]
102118
#[cfg(f16_enabled)]
103-
fn spec_tests_f16() {
104-
let cases = [
105-
(0.1, 1.0, Status::INEXACT),
106-
(-0.1, -0.0, Status::INEXACT),
107-
(0.9, 1.0, Status::INEXACT),
108-
(-0.9, -0.0, Status::INEXACT),
109-
(1.1, 2.0, Status::INEXACT),
110-
(-1.1, -1.0, Status::INEXACT),
111-
(1.9, 2.0, Status::INEXACT),
112-
(-1.9, -1.0, Status::INEXACT),
113-
];
114-
spec_test::<f16>(&cases);
115-
}
116-
117-
#[test]
118-
fn sanity_check_f32() {
119-
assert_eq!(ceil(1.1f32), 2.0);
120-
assert_eq!(ceil(2.9f32), 3.0);
121-
}
122-
123-
#[test]
124-
fn spec_tests_f32() {
125-
let cases = [
126-
(0.1, 1.0, Status::INEXACT),
127-
(-0.1, -0.0, Status::INEXACT),
128-
(0.9, 1.0, Status::INEXACT),
129-
(-0.9, -0.0, Status::INEXACT),
130-
(1.1, 2.0, Status::INEXACT),
131-
(-1.1, -1.0, Status::INEXACT),
132-
(1.9, 2.0, Status::INEXACT),
133-
(-1.9, -1.0, Status::INEXACT),
134-
];
135-
spec_test::<f32>(&cases);
119+
fn check_f16() {
120+
check::<f16>(&cases!(f16));
121+
check::<f16>(&[
122+
(hf16!("0x1p10"), hf16!("0x1p10"), Status::OK),
123+
(hf16!("-0x1p10"), hf16!("-0x1p10"), Status::OK),
124+
]);
136125
}
137126

138127
#[test]
139-
fn sanity_check_f64() {
140-
assert_eq!(ceil(1.1f64), 2.0);
141-
assert_eq!(ceil(2.9f64), 3.0);
128+
fn check_f32() {
129+
check::<f32>(&cases!(f32));
130+
check::<f32>(&[
131+
(hf32!("0x1p23"), hf32!("0x1p23"), Status::OK),
132+
(hf32!("-0x1p23"), hf32!("-0x1p23"), Status::OK),
133+
]);
142134
}
143135

144136
#[test]
145-
fn spec_tests_f64() {
146-
let cases = [
147-
(0.1, 1.0, Status::INEXACT),
148-
(-0.1, -0.0, Status::INEXACT),
149-
(0.9, 1.0, Status::INEXACT),
150-
(-0.9, -0.0, Status::INEXACT),
151-
(1.1, 2.0, Status::INEXACT),
152-
(-1.1, -1.0, Status::INEXACT),
153-
(1.9, 2.0, Status::INEXACT),
154-
(-1.9, -1.0, Status::INEXACT),
155-
];
156-
spec_test::<f64>(&cases);
137+
fn check_f64() {
138+
check::<f64>(&cases!(f64));
139+
check::<f64>(&[
140+
(hf64!("0x1p52"), hf64!("0x1p52"), Status::OK),
141+
(hf64!("-0x1p52"), hf64!("-0x1p52"), Status::OK),
142+
]);
157143
}
158144

159145
#[test]
160146
#[cfg(f128_enabled)]
161147
fn spec_tests_f128() {
162-
let cases = [
163-
(0.1, 1.0, Status::INEXACT),
164-
(-0.1, -0.0, Status::INEXACT),
165-
(0.9, 1.0, Status::INEXACT),
166-
(-0.9, -0.0, Status::INEXACT),
167-
(1.1, 2.0, Status::INEXACT),
168-
(-1.1, -1.0, Status::INEXACT),
169-
(1.9, 2.0, Status::INEXACT),
170-
(-1.9, -1.0, Status::INEXACT),
171-
];
172-
spec_test::<f128>(&cases);
148+
check::<f128>(&cases!(f128));
149+
check::<f128>(&[
150+
(hf128!("0x1p112"), hf128!("0x1p112"), Status::OK),
151+
(hf128!("-0x1p112"), hf128!("-0x1p112"), Status::OK),
152+
]);
173153
}
174154
}

libm/src/math/generic/floor.rs

Lines changed: 61 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub fn floor_status<F: Float>(x: F) -> FpResult<F> {
4646
F::from_bits(ix)
4747
} else {
4848
// |x| < 1.0, raise an inexact exception since truncation will happen.
49-
if ix & F::SIG_MASK == F::Int::ZERO {
49+
if ix & !F::SIGN_MASK == F::Int::ZERO {
5050
status = Status::OK;
5151
} else {
5252
status = Status::INEXACT;
@@ -72,86 +72,83 @@ mod tests {
7272
use super::*;
7373
use crate::support::Hexf;
7474

75-
/// Test against https://en.cppreference.com/w/cpp/numeric/math/floor
76-
fn spec_test<F: Float>(cases: &[(F, F, Status)]) {
77-
let roundtrip = [
78-
F::ZERO,
79-
F::ONE,
80-
F::NEG_ONE,
81-
F::NEG_ZERO,
82-
F::INFINITY,
83-
F::NEG_INFINITY,
84-
];
85-
86-
for x in roundtrip {
87-
let FpResult { val, status } = floor_status(x);
88-
assert_biteq!(val, x, "{}", Hexf(x));
89-
assert_eq!(status, Status::OK, "{}", Hexf(x));
90-
}
75+
macro_rules! cases {
76+
($f:ty) => {
77+
[
78+
// roundtrip
79+
(0.0, 0.0, Status::OK),
80+
(-0.0, -0.0, Status::OK),
81+
(1.0, 1.0, Status::OK),
82+
(-1.0, -1.0, Status::OK),
83+
(<$f>::INFINITY, <$f>::INFINITY, Status::OK),
84+
(<$f>::NEG_INFINITY, <$f>::NEG_INFINITY, Status::OK),
85+
// with rounding
86+
(0.1, 0.0, Status::INEXACT),
87+
(-0.1, -1.0, Status::INEXACT),
88+
(0.5, 0.0, Status::INEXACT),
89+
(-0.5, -1.0, Status::INEXACT),
90+
(0.9, 0.0, Status::INEXACT),
91+
(-0.9, -1.0, Status::INEXACT),
92+
(1.1, 1.0, Status::INEXACT),
93+
(-1.1, -2.0, Status::INEXACT),
94+
(1.5, 1.0, Status::INEXACT),
95+
(-1.5, -2.0, Status::INEXACT),
96+
(1.9, 1.0, Status::INEXACT),
97+
(-1.9, -2.0, Status::INEXACT),
98+
]
99+
};
100+
}
91101

92-
for &(x, res, res_stat) in cases {
102+
#[track_caller]
103+
fn check<F: Float>(cases: &[(F, F, Status)]) {
104+
for &(x, exp_res, exp_stat) in cases {
93105
let FpResult { val, status } = floor_status(x);
94-
assert_biteq!(val, res, "{}", Hexf(x));
95-
assert_eq!(status, res_stat, "{}", Hexf(x));
106+
assert_biteq!(val, exp_res, "{x:?} {}", Hexf(x));
107+
assert_eq!(
108+
status,
109+
exp_stat,
110+
"{x:?} {} -> {exp_res:?} {}",
111+
Hexf(x),
112+
Hexf(exp_res)
113+
);
96114
}
97115
}
98116

99-
/* Skipping f16 / f128 "sanity_check"s and spec cases due to rejected literal lexing at MSRV */
100-
101117
#[test]
102118
#[cfg(f16_enabled)]
103-
fn spec_tests_f16() {
104-
let cases = [];
105-
spec_test::<f16>(&cases);
106-
}
107-
108-
#[test]
109-
fn sanity_check_f32() {
110-
assert_eq!(floor(0.5f32), 0.0);
111-
assert_eq!(floor(1.1f32), 1.0);
112-
assert_eq!(floor(2.9f32), 2.0);
113-
}
114-
115-
#[test]
116-
fn spec_tests_f32() {
117-
let cases = [
118-
(0.1, 0.0, Status::INEXACT),
119-
(-0.1, -1.0, Status::INEXACT),
120-
(0.9, 0.0, Status::INEXACT),
121-
(-0.9, -1.0, Status::INEXACT),
122-
(1.1, 1.0, Status::INEXACT),
123-
(-1.1, -2.0, Status::INEXACT),
124-
(1.9, 1.0, Status::INEXACT),
125-
(-1.9, -2.0, Status::INEXACT),
126-
];
127-
spec_test::<f32>(&cases);
119+
fn check_f16() {
120+
check::<f16>(&cases!(f16));
121+
check::<f16>(&[
122+
(hf16!("0x1p10"), hf16!("0x1p10"), Status::OK),
123+
(hf16!("-0x1p10"), hf16!("-0x1p10"), Status::OK),
124+
]);
128125
}
129126

130127
#[test]
131-
fn sanity_check_f64() {
132-
assert_eq!(floor(1.1f64), 1.0);
133-
assert_eq!(floor(2.9f64), 2.0);
128+
fn check_f32() {
129+
check::<f32>(&cases!(f32));
130+
check::<f32>(&[
131+
(hf32!("0x1p23"), hf32!("0x1p23"), Status::OK),
132+
(hf32!("-0x1p23"), hf32!("-0x1p23"), Status::OK),
133+
]);
134134
}
135135

136136
#[test]
137-
fn spec_tests_f64() {
138-
let cases = [
139-
(0.1, 0.0, Status::INEXACT),
140-
(-0.1, -1.0, Status::INEXACT),
141-
(0.9, 0.0, Status::INEXACT),
142-
(-0.9, -1.0, Status::INEXACT),
143-
(1.1, 1.0, Status::INEXACT),
144-
(-1.1, -2.0, Status::INEXACT),
145-
(1.9, 1.0, Status::INEXACT),
146-
(-1.9, -2.0, Status::INEXACT),
147-
];
148-
spec_test::<f64>(&cases);
137+
fn check_f64() {
138+
check::<f64>(&cases!(f64));
139+
check::<f64>(&[
140+
(hf64!("0x1p52"), hf64!("0x1p52"), Status::OK),
141+
(hf64!("-0x1p52"), hf64!("-0x1p52"), Status::OK),
142+
]);
149143
}
150144

151145
#[test]
152146
#[cfg(f128_enabled)]
153147
fn spec_tests_f128() {
154-
let cases = [];
155-
spec_test::<f128>(&cases);
148+
check::<f128>(&cases!(f128));
149+
check::<f128>(&[
150+
(hf128!("0x1p112"), hf128!("0x1p112"), Status::OK),
151+
(hf128!("-0x1p112"), hf128!("-0x1p112"), Status::OK),
152+
]);
156153
}
157154
}

0 commit comments

Comments
 (0)