From 764760df62c63bc2a3d4bd4ec95c000233625834 Mon Sep 17 00:00:00 2001 From: mihael Date: Mon, 27 Apr 2026 16:48:55 +0200 Subject: [PATCH] `libzigc/math`: Implement `rintl`, `lrintl` (#31791) It's a fairly straightforward port of `musl`'s `rintl`, like `rint` and `rintf` were. `libc-test` tests for `rintl` are uncommented since they're now passing. I've also covered special cases for `rint` with tests, and broke down the current `rint` and `modf` test declarations into multiple -- so each libc function get its own test declaration at the very least. Contributes to #30978 Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31791 Reviewed-by: Andrew Kelley --- lib/c/math.zig | 33 ++++++- lib/compiler_rt/cos.zig | 2 +- lib/compiler_rt/long_double.zig | 37 ------- lib/compiler_rt/rem_pio2l.zig | 2 +- lib/compiler_rt/sin.zig | 2 +- lib/compiler_rt/sincos.zig | 2 +- lib/compiler_rt/tan.zig | 2 +- lib/libc/mingw/math/lrintl.c | 18 ---- lib/libc/mingw/math/rintl.c | 16 --- lib/libc/musl/src/math/i386/lrintl.c | 8 -- lib/libc/musl/src/math/i386/rintl.c | 7 -- lib/libc/musl/src/math/lrintl.c | 36 ------- lib/libc/musl/src/math/rintl.c | 29 ------ lib/libc/musl/src/math/s390x/rintl.c | 15 --- lib/libc/musl/src/math/x32/lrintl.s | 7 -- lib/libc/musl/src/math/x32/rintl.s | 6 -- lib/libc/musl/src/math/x86_64/lrintl.c | 8 -- lib/libc/musl/src/math/x86_64/rintl.c | 7 -- lib/std/math.zig | 1 + lib/std/math/float.zig | 62 ++++++++++++ src/libs/mingw.zig | 2 - src/libs/musl.zig | 9 -- src/libs/wasi_libc.zig | 2 - test/c/math.zig | 130 ++++++++++++++++++------- test/libc.zig | 2 +- 25 files changed, 195 insertions(+), 250 deletions(-) delete mode 100644 lib/compiler_rt/long_double.zig delete mode 100644 lib/libc/mingw/math/lrintl.c delete mode 100644 lib/libc/mingw/math/rintl.c delete mode 100644 lib/libc/musl/src/math/i386/lrintl.c delete mode 100644 lib/libc/musl/src/math/i386/rintl.c delete mode 100644 lib/libc/musl/src/math/lrintl.c delete mode 100644 lib/libc/musl/src/math/rintl.c delete mode 100644 lib/libc/musl/src/math/s390x/rintl.c delete mode 100644 lib/libc/musl/src/math/x32/lrintl.s delete mode 100644 lib/libc/musl/src/math/x32/rintl.s delete mode 100644 lib/libc/musl/src/math/x86_64/lrintl.c delete mode 100644 lib/libc/musl/src/math/x86_64/rintl.c diff --git a/lib/c/math.zig b/lib/c/math.zig index 20f0b70b5f769831b0e3400620302d9d4e919314..49190cc02952116e237fbc42a0cc934ee39df6de 100644 --- a/lib/c/math.zig +++ b/lib/c/math.zig @@ -2,6 +2,7 @@ const builtin = @import("builtin"); const std = @import("std"); const math = std.math; +const ld = math.long_double; const symbol = @import("../c.zig").symbol; @@ -35,7 +36,9 @@ comptime { symbol(&frexpl, "frexpl"); symbol(&hypotf, "hypotf"); symbol(&hypotl, "hypotl"); + symbol(&lrintl, "lrintl"); symbol(&modfl, "modfl"); + symbol(&rintl, "rintl"); } if ((builtin.target.isMinGW() and @sizeOf(f64) != @sizeOf(c_longdouble)) or builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) { @@ -254,11 +257,15 @@ fn isnanl(x: c_longdouble) callconv(.c) c_int { } fn lrint(x: f64) callconv(.c) c_long { - return @intFromFloat(rint(x)); + return @trunc(rint(x)); } fn lrintf(x: f32) callconv(.c) c_long { - return @intFromFloat(rintf(x)); + return @trunc(rintf(x)); +} + +fn lrintl(x: c_longdouble) callconv(.c) c_long { + return @trunc(rintl(x)); } fn modfGeneric(comptime T: type, x: T, iptr: *T) T { @@ -364,6 +371,28 @@ fn rintf(x: f32) callconv(.c) f32 { return y; } +fn rintl(x: c_longdouble) callconv(.c) c_longdouble { + if (@typeInfo(c_longdouble).float.bits == 64) + return rint(x); + + const toint: c_longdouble = 1 << math.floatFractionalBits(c_longdouble); + const se = ld.signExponent(x); + + if (se & 0x7fff >= 0x3fff + math.floatFractionalBits(c_longdouble)) + return x; + + var y: c_longdouble = undefined; + if ((se >> 15) == 1) { + y = x - toint + toint; + } else { + y = x + toint - toint; + } + + if (y == 0) + return 0 * x; + return y; +} + fn tanh(x: f64) callconv(.c) f64 { return math.tanh(x); } diff --git a/lib/compiler_rt/cos.zig b/lib/compiler_rt/cos.zig index 0bc02e32945fd33abd5a2c5b473398fe179c8ae2..a207f0244f941c3e6730f9e6f5891201840120cc 100644 --- a/lib/compiler_rt/cos.zig +++ b/lib/compiler_rt/cos.zig @@ -7,6 +7,7 @@ const std = @import("std"); const math = std.math; +const ld = math.long_double; const mem = std.mem; const expect = std.testing.expect; const expectApproxEqAbs = std.testing.expectApproxEqAbs; @@ -17,7 +18,6 @@ const trig = @import("trig.zig"); const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l; -const ld = @import("long_double.zig"); comptime { symbol(&cosh, "__cosh"); diff --git a/lib/compiler_rt/long_double.zig b/lib/compiler_rt/long_double.zig deleted file mode 100644 index 8c018b529fe90c4815f575c5b3c0bf447c1d04b2..0000000000000000000000000000000000000000 --- a/lib/compiler_rt/long_double.zig +++ /dev/null @@ -1,37 +0,0 @@ -//! Utilities for dealing with the `long double` type (`f80` or `f128`) - -const std = @import("std"); - -pub const U80 = std.meta.Int(.unsigned, 80); - -/// Returns the sign + exponent bits of a `long double` -pub fn signExponent(x: anytype) u16 { - const T = @TypeOf(x); - switch (T) { - f80 => { - const bits: U80 = @bitCast(x); - return @intCast(bits >> 64); - }, - f128 => { - const bits: u128 = @bitCast(x); - return @intCast(bits >> 112); - }, - else => @compileError("`signExponent` supports only `f80` and `f128`, got: " ++ @typeName(T)), - } -} - -/// Takes the top 16 bits of a `long double`'s mantissa -pub fn mantissaTop(x: anytype) u16 { - const T = @TypeOf(x); - switch (T) { - f80 => { - const bits: U80 = @bitCast(x); - return @intCast((bits >> 48) & 0xFFFF); - }, - f128 => { - const bits: u128 = @bitCast(x); - return @intCast((bits >> 96) & 0xFFFF); - }, - else => @compileError("`mantissaTop` supports only `f80` and `f128`, got: " ++ @typeName(T)), - } -} diff --git a/lib/compiler_rt/rem_pio2l.zig b/lib/compiler_rt/rem_pio2l.zig index fe878f116d91204b000f6fba1d2e5280b15723da..a6ff502500ae6a1365a7ea794a6d0bc6cd4421aa 100644 --- a/lib/compiler_rt/rem_pio2l.zig +++ b/lib/compiler_rt/rem_pio2l.zig @@ -5,8 +5,8 @@ const std = @import("std"); const math = std.math; +const ld = math.long_double; -const ld = @import("long_double.zig"); const rem_pio2_large = @import("rem_pio2_large.zig").rem_pio2_large; pub fn rem_pio2l(comptime T: type, x: T, y: *[2]T) i32 { diff --git a/lib/compiler_rt/sin.zig b/lib/compiler_rt/sin.zig index b3e46672eecc6276b5a24cdcec50f6a7f8c97018..040cba8cd3ef34e59f6bfd181a3c09036963fb4c 100644 --- a/lib/compiler_rt/sin.zig +++ b/lib/compiler_rt/sin.zig @@ -7,6 +7,7 @@ const std = @import("std"); const math = std.math; +const ld = math.long_double; const mem = std.mem; const expect = std.testing.expect; const expectApproxEqAbs = std.testing.expectApproxEqAbs; @@ -17,7 +18,6 @@ const trig = @import("trig.zig"); const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l; -const ld = @import("long_double.zig"); comptime { symbol(&sinh, "__sinh"); diff --git a/lib/compiler_rt/sincos.zig b/lib/compiler_rt/sincos.zig index 1ec9b81ac0c6b0611afe57c7b4176f257a3e3469..24bb751e76e7e93d32b05a4a801dec6d5d474709 100644 --- a/lib/compiler_rt/sincos.zig +++ b/lib/compiler_rt/sincos.zig @@ -2,6 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; const math = std.math; +const ld = math.long_double; const mem = std.mem; const expect = std.testing.expect; const expectApproxEqAbs = std.testing.expectApproxEqAbs; @@ -9,7 +10,6 @@ const trig = @import("trig.zig"); const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l; -const ld = @import("long_double.zig"); const compiler_rt = @import("../compiler_rt.zig"); const symbol = compiler_rt.symbol; diff --git a/lib/compiler_rt/tan.zig b/lib/compiler_rt/tan.zig index b9d996d8b94d2fc05cebdb213038be0c38a0e235..6cbc3cb098acd1f6a719451d103574b935a8bb2c 100644 --- a/lib/compiler_rt/tan.zig +++ b/lib/compiler_rt/tan.zig @@ -9,6 +9,7 @@ const std = @import("std"); const builtin = @import("builtin"); const math = std.math; +const ld = math.long_double; const mem = std.mem; const expect = std.testing.expect; const expectApproxEqAbs = std.testing.expectApproxEqAbs; @@ -17,7 +18,6 @@ const kernel = @import("trig.zig"); const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l; -const ld = @import("long_double.zig"); const arch = builtin.cpu.arch; const compiler_rt = @import("../compiler_rt.zig"); diff --git a/lib/libc/mingw/math/lrintl.c b/lib/libc/mingw/math/lrintl.c deleted file mode 100644 index 0bdd5784fe7e5fccf0a66ab3b271e539939a6602..0000000000000000000000000000000000000000 --- a/lib/libc/mingw/math/lrintl.c +++ /dev/null @@ -1,18 +0,0 @@ -/** - * This file has no copyright assigned and is placed in the Public Domain. - * This file is part of the mingw-w64 runtime package. - * No warranty is given; refer to the file DISCLAIMER.PD within this package. - */ -#include - -long lrintl (long double x) -{ - long retval = 0l; -#if __SIZEOF_LONG_DOUBLE__ == __SIZEOF_DOUBLE__ - retval = lrint(x); -#elif defined(_AMD64_) || defined(__x86_64__) || defined(_X86_) || defined(__i386__) - __asm__ __volatile__ ("fistpl %0" : "=m" (retval) : "t" (x) : "st"); -#endif - return retval; -} - diff --git a/lib/libc/mingw/math/rintl.c b/lib/libc/mingw/math/rintl.c deleted file mode 100644 index da30c7aa2e48ac0ffadfd1fde8873dede866dc8b..0000000000000000000000000000000000000000 --- a/lib/libc/mingw/math/rintl.c +++ /dev/null @@ -1,16 +0,0 @@ -/** - * This file has no copyright assigned and is placed in the Public Domain. - * This file is part of the mingw-w64 runtime package. - * No warranty is given; refer to the file DISCLAIMER.PD within this package. - */ -#include - -long double rintl (long double x) { - long double retval = 0.0L; -#if __SIZEOF_LONG_DOUBLE__ == __SIZEOF_DOUBLE__ - retval = rint(x); -#elif defined(_AMD64_) || defined(__x86_64__) || defined(_X86_) || defined(__i386__) - __asm__ __volatile__ ("frndint;": "=t" (retval) : "0" (x)); -#endif - return retval; -} diff --git a/lib/libc/musl/src/math/i386/lrintl.c b/lib/libc/musl/src/math/i386/lrintl.c deleted file mode 100644 index eb8c090288199537d43819880d449e81d7b8f43c..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/i386/lrintl.c +++ /dev/null @@ -1,8 +0,0 @@ -#include - -long lrintl(long double x) -{ - long r; - __asm__ ("fistpl %0" : "=m"(r) : "t"(x) : "st"); - return r; -} diff --git a/lib/libc/musl/src/math/i386/rintl.c b/lib/libc/musl/src/math/i386/rintl.c deleted file mode 100644 index e1a92077f558c29f41f1d88ac9c0ade81dbd678d..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/i386/rintl.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -long double rintl(long double x) -{ - __asm__ ("frndint" : "+t"(x)); - return x; -} diff --git a/lib/libc/musl/src/math/lrintl.c b/lib/libc/musl/src/math/lrintl.c deleted file mode 100644 index b2a8106d7c6a1710eea10791fdf72d88df541d95..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/lrintl.c +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include "libm.h" - - -#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 -long lrintl(long double x) -{ - return lrint(x); -} -#elif defined(FE_INEXACT) -/* -see comments in lrint.c - -Note that if LONG_MAX == 0x7fffffffffffffff && LDBL_MANT_DIG == 64 -then x == 2**63 - 0.5 is the only input that overflows and -raises inexact (with tonearest or upward rounding mode) -*/ -long lrintl(long double x) -{ - #pragma STDC FENV_ACCESS ON - int e; - - e = fetestexcept(FE_INEXACT); - x = rintl(x); - if (!e && (x > LONG_MAX || x < LONG_MIN)) - feclearexcept(FE_INEXACT); - /* conversion */ - return x; -} -#else -long lrintl(long double x) -{ - return rintl(x); -} -#endif diff --git a/lib/libc/musl/src/math/rintl.c b/lib/libc/musl/src/math/rintl.c deleted file mode 100644 index 374327db2282d450ceb79e12286db767eb452148..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/rintl.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "libm.h" - -#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 -long double rintl(long double x) -{ - return rint(x); -} -#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384 - -static const long double toint = 1/LDBL_EPSILON; - -long double rintl(long double x) -{ - union ldshape u = {x}; - int e = u.i.se & 0x7fff; - int s = u.i.se >> 15; - long double y; - - if (e >= 0x3fff+LDBL_MANT_DIG-1) - return x; - if (s) - y = x - toint + toint; - else - y = x + toint - toint; - if (y == 0) - return 0*x; - return y; -} -#endif diff --git a/lib/libc/musl/src/math/s390x/rintl.c b/lib/libc/musl/src/math/s390x/rintl.c deleted file mode 100644 index bae53185a27c700e074e8d21c782dab71cdaff99..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/s390x/rintl.c +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#if defined(__HTM__) || __ARCH__ >= 9 - -long double rintl(long double x) -{ - __asm__ ("fixbr %0, 0, %1" : "=f"(x) : "f"(x)); - return x; -} - -#else - -#include "../rintl.c" - -#endif diff --git a/lib/libc/musl/src/math/x32/lrintl.s b/lib/libc/musl/src/math/x32/lrintl.s deleted file mode 100644 index d4355c327c7a0db7daec41a04120d6147a654036..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/x32/lrintl.s +++ /dev/null @@ -1,7 +0,0 @@ -.global lrintl -.type lrintl,@function -lrintl: - fldt 8(%esp) - fistpl 8(%esp) - movl 8(%esp),%eax - ret diff --git a/lib/libc/musl/src/math/x32/rintl.s b/lib/libc/musl/src/math/x32/rintl.s deleted file mode 100644 index be1d2fa735e3b948673d80d3d59001fcacc6c14f..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/x32/rintl.s +++ /dev/null @@ -1,6 +0,0 @@ -.global rintl -.type rintl,@function -rintl: - fldt 8(%esp) - frndint - ret diff --git a/lib/libc/musl/src/math/x86_64/lrintl.c b/lib/libc/musl/src/math/x86_64/lrintl.c deleted file mode 100644 index 068e2e4d62cf6102e357f0009c063b2df42088c7..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/x86_64/lrintl.c +++ /dev/null @@ -1,8 +0,0 @@ -#include - -long lrintl(long double x) -{ - long r; - __asm__ ("fistpll %0" : "=m"(r) : "t"(x) : "st"); - return r; -} diff --git a/lib/libc/musl/src/math/x86_64/rintl.c b/lib/libc/musl/src/math/x86_64/rintl.c deleted file mode 100644 index e1a92077f558c29f41f1d88ac9c0ade81dbd678d..0000000000000000000000000000000000000000 --- a/lib/libc/musl/src/math/x86_64/rintl.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -long double rintl(long double x) -{ - __asm__ ("frndint" : "+t"(x)); - return x; -} diff --git a/lib/std/math.zig b/lib/std/math.zig index 56fd14cd33410dd47656db8117248700e7ca9939..da535bcc472846ccc9bd318c285449811935100f 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -58,6 +58,7 @@ pub const floatMax = float.floatMax; pub const floatEps = float.floatEps; pub const floatEpsAt = float.floatEpsAt; pub const inf = float.inf; +pub const long_double = float.long_double; pub const nan = float.nan; pub const snan = float.snan; diff --git a/lib/std/math/float.zig b/lib/std/math/float.zig index 39ef854b5b0b42f14afeb86ad02b09d2fe85bce3..535f0b3ba64eb5f6a31477a5275f89d694cad20c 100644 --- a/lib/std/math/float.zig +++ b/lib/std/math/float.zig @@ -4,6 +4,68 @@ const assert = std.debug.assert; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; +/// A namespace for functions that deal with floats that provide greater than +/// double precision (`f80`, `f128`, `c_longdouble`). Commonly referred to as +/// `long double` in C. +pub const long_double = struct { + const U80 = @Int(.unsigned, 80); + + inline fn bitWidth(x: anytype) u16 { + const T = @TypeOf(x); + return switch (T) { + f80, f128, c_longdouble => @typeInfo(T).float.bits, + else => @compileError("Unsupported type: " ++ @typeName(T) ++ "\nPass a `f80`, `f128`, or `c_longdouble`."), + }; + } + + /// Returns the sign + exponent bits of a `long double`. + pub fn signExponent(x: anytype) u16 { + const bit_width = bitWidth(x); + switch (bit_width) { + 80 => { + const bits: U80 = @bitCast(x); + return @intCast(bits >> 64); + }, + 128 => { + const bits: u128 = @bitCast(x); + return @intCast(bits >> 112); + }, + // `c_longdouble` can have <80 bits on some targets, we want to error on that + else => @compileError(std.fmt.comptimePrint("`signExponent` supports floats of only `80` and `128` bit width, got bit width: {d}", .{bit_width})), + } + } + + test "signExponent" { + try expectEqual(signExponent(@as(f80, -0.0)), 0x8000); + try expectEqual(signExponent(@as(f128, 0.0)), 0x0000); + try expectEqual(signExponent(@as(f128, 42.0)), 0x4004); + try expectEqual(signExponent(nan(c_longdouble)), 0x7FFF); + } + + /// Takes the top 16 bits of a `long double`'s mantissa. + pub fn mantissaTop(x: anytype) u16 { + const bit_width = bitWidth(x); + switch (bit_width) { + 80 => { + const bits: U80 = @bitCast(x); + return @intCast((bits >> 48) & 0xFFFF); + }, + 128 => { + const bits: u128 = @bitCast(x); + return @intCast((bits >> 96) & 0xFFFF); + }, + // `c_longdouble` can have <80 bits on some targets, we want to error on that + else => @compileError(std.fmt.comptimePrint("`mantissaTop` supports floats of only `80` and `128` bit width, got bit width: {d}", .{bit_width})), + } + } + + test "mantissaTop" { + try expectEqual(mantissaTop(@as(f80, -0.0)), 0x0000); + try expectEqual(mantissaTop(nan(f128)), 0x8000); + try expectEqual(mantissaTop(@as(f128, 42.0)), 0x5000); + } +}; + pub fn FloatRepr(comptime Float: type) type { const fractional_bits = floatFractionalBits(Float); const exponent_bits = floatExponentBits(Float); diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig index 9ea43035bbd3886211e353b29ead0c89ae7cc73b..4dfc8107fb3bd5fadac55f1a4d390c9d7631f6fa 100644 --- a/src/libs/mingw.zig +++ b/src/libs/mingw.zig @@ -853,9 +853,7 @@ const mingw32_x86_src = [_][]const u8{ "math" ++ path.sep_str ++ "fmal.c", "math" ++ path.sep_str ++ "llrintl.c", "math" ++ path.sep_str ++ "llroundl.c", - "math" ++ path.sep_str ++ "lrintl.c", "math" ++ path.sep_str ++ "lroundl.c", - "math" ++ path.sep_str ++ "rintl.c", "math" ++ path.sep_str ++ "tgammal.c", "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "_chgsignl.S", "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acoshl.c", diff --git a/src/libs/musl.zig b/src/libs/musl.zig index 5f04f32b9626d03ed48949da15fae15e02ea8b12..c9bfd258a7a43f1e5806b5bebd22809b213df9cc 100644 --- a/src/libs/musl.zig +++ b/src/libs/musl.zig @@ -850,14 +850,12 @@ const src_files = [_][]const u8{ "musl/src/math/i386/log1p.s", "musl/src/math/i386/log2l.s", "musl/src/math/i386/logl.s", - "musl/src/math/i386/lrintl.c", "musl/src/math/i386/remainder.c", "musl/src/math/i386/remainderf.c", "musl/src/math/i386/remainderl.c", "musl/src/math/i386/remquof.s", "musl/src/math/i386/remquol.s", "musl/src/math/i386/remquo.s", - "musl/src/math/i386/rintl.c", "musl/src/math/i386/scalblnf.s", "musl/src/math/i386/scalblnl.s", "musl/src/math/i386/scalbln.s", @@ -897,7 +895,6 @@ const src_files = [_][]const u8{ "musl/src/math/logbf.c", "musl/src/math/logbl.c", "musl/src/math/logl.c", - "musl/src/math/lrintl.c", "musl/src/math/lround.c", "musl/src/math/lroundf.c", "musl/src/math/lroundl.c", @@ -943,7 +940,6 @@ const src_files = [_][]const u8{ "musl/src/math/remquo.c", "musl/src/math/remquof.c", "musl/src/math/remquol.c", - "musl/src/math/rintl.c", "musl/src/math/riscv32/fma.c", "musl/src/math/riscv32/fmaf.c", "musl/src/math/riscv64/fma.c", @@ -953,7 +949,6 @@ const src_files = [_][]const u8{ "musl/src/math/s390x/nearbyint.c", "musl/src/math/s390x/nearbyintf.c", "musl/src/math/s390x/nearbyintl.c", - "musl/src/math/s390x/rintl.c", "musl/src/math/scalb.c", "musl/src/math/scalbf.c", "musl/src/math/scalbln.c", @@ -995,9 +990,7 @@ const src_files = [_][]const u8{ "musl/src/math/x32/log1pl.s", "musl/src/math/x32/log2l.s", "musl/src/math/x32/logl.s", - "musl/src/math/x32/lrintl.s", "musl/src/math/x32/remainderl.s", - "musl/src/math/x32/rintl.s", "musl/src/math/x86_64/acosl.s", "musl/src/math/x86_64/asinl.s", "musl/src/math/x86_64/atan2l.s", @@ -1014,10 +1007,8 @@ const src_files = [_][]const u8{ "musl/src/math/x86_64/log1pl.s", "musl/src/math/x86_64/log2l.s", "musl/src/math/x86_64/logl.s", - "musl/src/math/x86_64/lrintl.c", "musl/src/math/x86_64/remainderl.c", "musl/src/math/x86_64/remquol.c", - "musl/src/math/x86_64/rintl.c", "musl/src/misc/a64l.c", "musl/src/misc/basename.c", "musl/src/misc/dirname.c", diff --git a/src/libs/wasi_libc.zig b/src/libs/wasi_libc.zig index 07f316b635e5fd3dc03c45ca83d664f650714d5a..8c328cc21cb9f4d7fdf5131a5505740d8d9e6a6b 100644 --- a/src/libs/wasi_libc.zig +++ b/src/libs/wasi_libc.zig @@ -727,7 +727,6 @@ const libc_top_half_src_files = [_][]const u8{ "musl/src/math/logbf.c", "musl/src/math/logbl.c", "musl/src/math/logl.c", - "musl/src/math/lrintl.c", "musl/src/math/lround.c", "musl/src/math/lroundf.c", "musl/src/math/lroundl.c", @@ -761,7 +760,6 @@ const libc_top_half_src_files = [_][]const u8{ "musl/src/math/remquo.c", "musl/src/math/remquof.c", "musl/src/math/remquol.c", - "musl/src/math/rintl.c", "musl/src/math/scalb.c", "musl/src/math/scalbf.c", "musl/src/math/scalbln.c", diff --git a/test/c/math.zig b/test/c/math.zig index fe17e23abea158df1e19c5ca4fd502d848f87ba7..b365bc9ac43e94177f332c4e8374b485e1a79987 100644 --- a/test/c/math.zig +++ b/test/c/math.zig @@ -3,14 +3,19 @@ const std = @import("std"); const c = std.c; const math = std.math; + const testing = std.testing; +const expect = testing.expect; +const expectEqual = testing.expectEqual; +const expectApproxEqAbs = testing.expectApproxEqAbs; +const expectApproxEqRel = testing.expectApproxEqRel; fn testModf(comptime T: type) !void { const f = switch (T) { f32 => c.modff, f64 => c.modf, c_longdouble => c.modfl, - else => unreachable, + else => @compileError("modf not implemented for " ++ @typeName(T)), }; var int: T = undefined; @@ -20,85 +25,140 @@ fn testModf(comptime T: type) !void { const normal_frac = f(@as(T, 1234.567), iptr); // Account for precision error const expected = 1234.567 - @as(T, 1234); - try testing.expectApproxEqAbs(expected, normal_frac, eps_val); - try testing.expectApproxEqRel(@as(T, 1234.0), iptr.*, eps_val); + try expectApproxEqAbs(expected, normal_frac, eps_val); + try expectApproxEqRel(@as(T, 1234.0), iptr.*, eps_val); // When `x` is a NaN, NaN is returned and `*iptr` is set to NaN const nan_frac = f(math.nan(T), iptr); - try testing.expect(math.isNan(nan_frac)); - try testing.expect(math.isNan(iptr.*)); + try expect(math.isNan(nan_frac)); + try expect(math.isNan(iptr.*)); // When `x` is positive infinity, +0 is returned and `*iptr` is set to // positive infinity const pos_zero_frac = f(math.inf(T), iptr); - try testing.expect(math.isPositiveZero(pos_zero_frac)); - try testing.expect(math.isPositiveInf(iptr.*)); + try expectEqual(0.0, pos_zero_frac); + try expect(math.isPositiveInf(iptr.*)); // When `x` is negative infinity, -0 is returned and `*iptr` is set to // negative infinity const neg_zero_frac = f(-math.inf(T), iptr); - try testing.expect(math.isNegativeZero(neg_zero_frac)); - try testing.expect(math.isNegativeInf(iptr.*)); + try expectEqual(-0.0, neg_zero_frac); + try expect(math.isNegativeInf(iptr.*)); // Return -0 when `x` is a negative integer const nz_frac = f(@as(T, -1000.0), iptr); - try testing.expect(math.isNegativeZero(nz_frac)); - try testing.expectEqual(@as(T, -1000.0), iptr.*); + try expectEqual(-0.0, nz_frac); + try expectEqual(@as(T, -1000.0), iptr.*); // Return +0 when `x` is a positive integer const pz_frac = f(@as(T, 1000.0), iptr); - try testing.expect(math.isPositiveZero(pz_frac)); - try testing.expectEqual(@as(T, 1000.0), iptr.*); + try expectEqual(0.0, pz_frac); + try expectEqual(@as(T, 1000.0), iptr.*); } test "modf" { - try testModf(f32); try testModf(f64); +} - if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO +test "modff" { + try testModf(f32); +} + +test "modfl" { + if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO: see https://codeberg.org/ziglang/zig/issues/30976 try testModf(c_longdouble); } -fn testRint(comptime T: type) !void { +fn testRintSpecial(comptime T: type) !void { const f = switch (T) { f32 => c.rintf, f64 => c.rint, + c_longdouble => c.rintl, + else => @compileError("rint not implemented for" ++ @typeName(T)), + }; + + // For the special cases, x itself should be returned + try expectEqual(0.0, f(0.0)); + try expectEqual(-0.0, f(-0.0)); + try expectEqual(math.inf(T), f(math.inf(T))); + try expectEqual(-math.inf(T), f(-math.inf(T))); + try expect(math.isNan(f(math.nan(T)))); +} + +fn testRintNormal(comptime T: type) !void { + const f = switch (T) { + f32 => c.rintf, + f64 => c.rint, + c_longdouble => c.rintl, else => @compileError("rint not implemented for" ++ @typeName(T)), }; // Positive numbers round correctly - try testing.expectEqual(@as(T, 42.0), f(42.2)); - try testing.expectEqual(@as(T, 42.0), f(41.8)); + try expectEqual(@as(T, 42.0), f(42.2)); + try expectEqual(@as(T, 42.0), f(41.8)); + try expectEqual(@as(T, 16_777_216.0), f(16_777_215.6)); // Negative numbers round correctly - try testing.expectEqual(@as(T, -6.0), f(-5.9)); - try testing.expectEqual(@as(T, -6.0), f(-6.1)); + try expectEqual(@as(T, -6.0), f(-5.9)); + try expectEqual(@as(T, -6.0), f(-6.1)); + // TODO: negative `long double`s close to `-n.5` seem to round to `-n.5` + // instead of either `-n.0` or `-(n-1).0` on NetBSD. For example, this + // case would round to `-16_777_215.5`. + if (!(T == c_longdouble and builtin.target.os.tag == .netbsd)) { + try expectEqual(@as(T, -16_777_215.0), f(-16_777_215.4)); + } // No rounding needed test - try testing.expectEqual(@as(T, 5.0), f(5.0)); - try testing.expectEqual(@as(T, -10.0), f(-10.0)); - try testing.expectEqual(@as(T, 0.0), f(0.0)); + try expectEqual(@as(T, 5.0), f(5.0)); + try expectEqual(@as(T, -10.0), f(-10.0)); + try expectEqual(@as(T, 0.0), f(0.0)); // Very large numbers return unchanged const large: T = 9007199254740992.0; // 2^53 - try testing.expectEqual(large, f(large)); - try testing.expectEqual(-large, f(-large)); + try expectEqual(large, f(large)); + try expectEqual(-large, f(-large)); // Small positive numbers round to zero - const pos_result = f(0.3); - try testing.expect(math.isPositiveZero(pos_result)); + try expectEqual(@as(T, 0.0), f(0.3)); - // Small negative numbers round to negative zero - const neg_result = f(-0.3); - try testing.expect(math.isNegativeZero(neg_result)); + // TODO: negative `long double`s close to `-n.5` seem to round to `-n.5` + // instead of either `-n.0` or `-(n-1).0` on NetBSD. For example, this + // case would round to `-0.5`. + if (!(T == c_longdouble and builtin.target.os.tag == .netbsd)) { + // Small negative numbers round to negative zero + try expectEqual(@as(T, -0.0), f(-0.3)); + } // Exact half rounds to nearest even (banker's rounding) - try testing.expectEqual(@as(T, 2.0), f(2.5)); - try testing.expectEqual(@as(T, 4.0), f(3.5)); + try expectEqual(@as(T, 2.0), f(2.5)); + try expectEqual(@as(T, 4.0), f(3.5)); } -test "rint" { - try testRint(f32); - try testRint(f64); +test "rintf.special" { + try testRintSpecial(f32); +} + +test "rintf.normal" { + try testRintNormal(f32); +} + +test "rint.special" { + try testRintSpecial(f64); +} + +test "rint.normal" { + try testRintNormal(f64); +} + +test "rintl.special" { + if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO: see https://codeberg.org/ziglang/zig/issues/30976 + + try testRintSpecial(c_longdouble); +} + +test "rintl.normal" { + if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO: see https://codeberg.org/ziglang/zig/issues/30976 + + try testRintNormal(c_longdouble); } diff --git a/test/libc.zig b/test/libc.zig index bd10130c99141cb720f9696c363e08c93ce627d4..416b1100f45d83826a05983253c0d41d76b41053 100644 --- a/test/libc.zig +++ b/test/libc.zig @@ -295,7 +295,7 @@ pub fn addCases(cases: *tests.LibcContext) void { cases.addLibcTestCase("math/remquol.c", true, .{}); cases.addLibcTestCase("math/rint.c", true, .{}); cases.addLibcTestCase("math/rintf.c", true, .{}); - // cases.addLibcTestCase("math/rintl.c", true, .{}); + cases.addLibcTestCase("math/rintl.c", true, .{}); cases.addLibcTestCase("math/round.c", true, .{}); cases.addLibcTestCase("math/roundf.c", true, .{}); cases.addLibcTestCase("math/roundl.c", true, .{}); -- 2.54.0