/* SPDX-License-Identifier: MIT */ /* origin: musl src/math/rint.c */ use super::super::Float; use super::super::support::{FpResult, Round}; /// IEEE 754-2019 `roundToIntegralExact`, which respects rounding mode and raises inexact if /// applicable. #[inline] pub fn rint_round(x: F, _round: Round) -> FpResult { 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(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::(&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::(&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::(&cases); } #[test] #[cfg(f128_enabled)] fn spec_tests_f128() { let cases = []; spec_test::(&cases); } }