From 952ae4d0d8eb07bd7770b288c3341d7ce454a5cc Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 18 Jan 2026 12:53:12 +0200 Subject: [PATCH] math: rewrite round() to match closely the Go version (and mpfr's one), update test (#26381) --- vlib/math/floor.v | 34 +++++++++++++++++----------------- vlib/math/math_test.v | 3 ++- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/vlib/math/floor.v b/vlib/math/floor.v index b2f6fd6fa..5660689b1 100644 --- a/vlib/math/floor.v +++ b/vlib/math/floor.v @@ -77,25 +77,25 @@ pub fn trunc(x f64) f64 { // round(±inf) = ±inf // round(nan) = nan pub fn round(x f64) f64 { - if x == 0 || is_nan(x) || is_inf(x, 0) { - return x - } - // Largest integer <= x - mut y := floor(x) // Fractional part - mut r := x - y // Round up to nearest. - if r > 0.5 { - y += 1.0 - return y - } - // Round to even - if r == 0.5 { - r = y - 2.0 * floor(0.5 * y) - if r == 1.0 { - y += 1.0 + mut bits := f64_bits(x) + mut e_ := (bits >> shift) & mask + if e_ < bias { + // Round abs(x) < 1 including denormals. + bits &= sign_mask // +-0 + if e_ == bias - 1 { + bits |= uvone // +-1 } + } else if e_ < bias + shift { + // Round any abs(x) >= 1 containing a fractional component [0,1). + // + // Numbers with larger exponents are returned unchanged since they + // must be either an integer, infinity, or NaN. + half := u64(1) << (shift - 1) + e_ -= bias + bits += half >> e_ + bits &= ~(frac_mask >> e_) } - // Else round down. - return y + return f64_from_bits(bits) } // Returns the rounded float, with sig_digits of precision. diff --git a/vlib/math/math_test.v b/vlib/math/math_test.v index 442578f88..ea7d9671f 100644 --- a/vlib/math/math_test.v +++ b/vlib/math/math_test.v @@ -847,7 +847,8 @@ fn test_round() { f := round(vf_[i]) assert alike(round_[i], f) } - vfround_sc_ := [[f64(0), 0], [nan(), nan()], [inf(1), inf(1)]] + vfround_sc_ := [[f64(0), 0], [-0.5, -1.0], [nan(), nan()], + [inf(1), inf(1)]] // vfround_even_sc_ := [[f64(0), 0], [f64(1.390671161567e-309), 0], // denormal // [f64(0.49999999999999994), 0], // 0.5-epsilon [f64(0.5), 0], // [f64(0.5000000000000001), 1], // 0.5+epsilon [f64(-1.5), -2], -- 2.39.5