131 lines
3.7 KiB
Rust
131 lines
3.7 KiB
Rust
/* 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);
|
|
}
|
|
}
|