blob: c5bc27d3de6bc62cd861272550765161fe4a3a55 [file] [log] [blame]
/* SPDX-License-Identifier: MIT */
/* origin: musl src/math/rint.c */
use crate::support::{Float, FpResult, Round};
/// IEEE 754-2019 `roundToIntegralExact`, which respects rounding mode and raises inexact if
/// applicable.
#[inline]
pub fn rint_round<F: Float>(x: F, _round: Round) -> FpResult<F> {
let toint = F::ONE / F::EPSILON;
let e = x.ex();
let positive = x.is_sign_positive();
// On i386 `force_eval!` must be used to force rounding via storage to memory. Otherwise,
// the excess precission from x87 would cause an incorrect final result.
let force = |x| {
if cfg!(x86_no_sse) && (F::BITS == 32 || F::BITS == 64) {
force_eval!(x)
} else {
x
}
};
let res = if e >= F::EXP_BIAS + F::SIG_BITS {
// No fractional part; exact result can be returned.
x
} else {
// Apply a net-zero adjustment that nudges `y` in the direction of the rounding mode. For
// Rust this is always nearest, but ideally it would take `round` into account.
let y = if positive {
force(force(x) + toint) - toint
} else {
force(force(x) - toint) + toint
};
if y == F::ZERO {
// A zero result takes the sign of the input.
if positive { F::ZERO } else { F::NEG_ZERO }
} else {
y
}
};
FpResult::ok(res)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::support::{Hexf, Status};
fn spec_test<F: Float>(cases: &[(F, F, Status)]) {
let roundtrip = [
F::ZERO,
F::ONE,
F::NEG_ONE,
F::NEG_ZERO,
F::INFINITY,
F::NEG_INFINITY,
];
for x in roundtrip {
let FpResult { val, status } = rint_round(x, Round::Nearest);
assert_biteq!(val, x, "rint_round({})", Hexf(x));
assert_eq!(status, Status::OK, "{}", Hexf(x));
}
for &(x, res, res_stat) in cases {
let FpResult { val, status } = rint_round(x, Round::Nearest);
assert_biteq!(val, res, "rint_round({})", Hexf(x));
assert_eq!(status, res_stat, "{}", Hexf(x));
}
}
#[test]
#[cfg(f16_enabled)]
fn spec_tests_f16() {
let cases = [];
spec_test::<f16>(&cases);
}
#[test]
fn spec_tests_f32() {
let cases = [
(0.1, 0.0, Status::OK),
(-0.1, -0.0, Status::OK),
(0.5, 0.0, Status::OK),
(-0.5, -0.0, Status::OK),
(0.9, 1.0, Status::OK),
(-0.9, -1.0, Status::OK),
(1.1, 1.0, Status::OK),
(-1.1, -1.0, Status::OK),
(1.5, 2.0, Status::OK),
(-1.5, -2.0, Status::OK),
(1.9, 2.0, Status::OK),
(-1.9, -2.0, Status::OK),
(2.8, 3.0, Status::OK),
(-2.8, -3.0, Status::OK),
];
spec_test::<f32>(&cases);
}
#[test]
fn spec_tests_f64() {
let cases = [
(0.1, 0.0, Status::OK),
(-0.1, -0.0, Status::OK),
(0.5, 0.0, Status::OK),
(-0.5, -0.0, Status::OK),
(0.9, 1.0, Status::OK),
(-0.9, -1.0, Status::OK),
(1.1, 1.0, Status::OK),
(-1.1, -1.0, Status::OK),
(1.5, 2.0, Status::OK),
(-1.5, -2.0, Status::OK),
(1.9, 2.0, Status::OK),
(-1.9, -2.0, Status::OK),
(2.8, 3.0, Status::OK),
(-2.8, -3.0, Status::OK),
];
spec_test::<f64>(&cases);
}
#[test]
#[cfg(f128_enabled)]
fn spec_tests_f128() {
let cases = [];
spec_test::<f128>(&cases);
}
}