/* SPDX-License-Identifier: MIT */
/* origin: musl src/math/rint.c */

use crate::support::{Float, FpResult, Status};

/// IEEE 754-2019 `roundToIntegralExact`, which respects rounding mode and raises inexact if
/// applicable.
#[inline]
pub fn rint_status<F: Float>(x: F) -> 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_sse2) && (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
        }
    };

    let status = if res == x {
        Status::OK
    } else {
        Status::INEXACT
    };
    FpResult::new(res, status)
}
