From 014178725744eda46db04ccfe44187ef616ceaf7 Mon Sep 17 00:00:00 2001 From: mihael Date: Sun, 15 Mar 2026 20:24:28 +0100 Subject: [PATCH] `libzigc`: implement `modff` `modf` function was generalized and renamed to `modfGeneric`, `modf` and `modff` provide the appropriate type while calling that function. The unit tests were also generalized so they can be reused for different float types. Both `modf` and `modff` were tested after making these changes: ``` $ stage4/bin/zig build test-libc -Dlibc-test-path= -Dtest-filter=modf -fqemu -fwasmtime --summary line Build Summary: 921/921 steps succeeded ``` ``` stage4/bin/zig build test-libc -Dlibc-test-path= -Dtest-filter=modff -fqemu -fwasmtime --summary line Build Summary: 369/369 steps succeeded ``` --- lib/c/math.zig | 64 +++++++++++++++++++++++----------- lib/libc/mingw/math/modff.c | 42 ---------------------- lib/libc/musl/src/math/modff.c | 34 ------------------ src/libs/mingw.zig | 1 - src/libs/musl.zig | 1 - src/libs/wasi_libc.zig | 1 - 6 files changed, 44 insertions(+), 99 deletions(-) delete mode 100644 lib/libc/mingw/math/modff.c delete mode 100644 lib/libc/musl/src/math/modff.c diff --git a/lib/c/math.zig b/lib/c/math.zig index 2593d5bf86..497154ea04 100644 --- a/lib/c/math.zig +++ b/lib/c/math.zig @@ -4,6 +4,7 @@ const std = @import("std"); const math = std.math; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; +const expectApproxEqAbs = std.testing.expectApproxEqAbs; const expectApproxEqRel = std.testing.expectApproxEqRel; const symbol = @import("../c.zig").symbol; @@ -37,6 +38,7 @@ comptime { symbol(&coshf, "coshf"); symbol(&hypotf, "hypotf"); symbol(&hypotl, "hypotl"); + symbol(&modff, "modff"); symbol(&nan, "nan"); symbol(&nanf, "nanf"); symbol(&nanl, "nanl"); @@ -166,66 +168,88 @@ fn isnanl(x: c_longdouble) callconv(.c) c_int { return if (math.isNan(x)) 1 else 0; } -fn modf(x: f64, iptr: *f64) callconv(.c) f64 { +fn modfGeneric(comptime T: type, x: T, iptr: *T) T { if (math.isNegativeInf(x)) { - iptr.* = -math.inf(f64); + iptr.* = -math.inf(T); return -0.0; } if (math.isPositiveInf(x)) { - iptr.* = math.inf(f64); + iptr.* = math.inf(T); return 0.0; } // Avoids raising the INVALID flag on qemu-riscv if (math.isNan(x)) { - iptr.* = math.nan(f64); - return math.nan(f64); + iptr.* = math.nan(T); + return math.nan(T); } const r = math.modf(x); iptr.* = r.ipart; - // If the result would be a negative zero, we must be explicit about + // If the result is a negative zero, we must be explicit about // returning a negative zero. return if (math.isNegativeZero(x) or (x < 0.0 and x == r.ipart)) -0.0 else r.fpart; } -test "modf" { - var int: f64 = undefined; - const iptr = ∫ - const eps_val = 1e-6; +fn modf(x: f64, iptr: *f64) callconv(.c) f64 { + return modfGeneric(f64, x, iptr); +} - const normal_frac = modf(1234.5678, iptr); - try expectApproxEqRel(0.5678, normal_frac, eps_val); - try expectApproxEqRel(1234.0, iptr.*, eps_val); +fn modff(x: f32, iptr: *f32) callconv(.c) f32 { + return modfGeneric(f32, x, iptr); +} + +fn testModf(comptime T: type) !void { + // Choose the appropriate `modf` impl to test based on type + const f = switch (T) { + f64 => modf, + f32 => modff, + else => @compileError("modf not implemented for " ++ @typeName(T)), + }; + + var int: T = undefined; + const iptr = ∫ + const eps_val: comptime_float = @max(1e-6, math.floatEps(T)); + + const normal_frac = f(@as(T, 1234.567), iptr); + // Account for precision error + const expected = 1234.567 - @as(T, 1234); + 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 = modf(math.nan(f64), iptr); + const nan_frac = f(math.nan(T), 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 = modf(math.inf(f64), iptr); + const pos_zero_frac = f(math.inf(T), iptr); try expect(math.isPositiveZero(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 = modf(-math.inf(f64), iptr); + const neg_zero_frac = f(-math.inf(T), iptr); try expect(math.isNegativeZero(neg_zero_frac)); try expect(math.isNegativeInf(iptr.*)); // Return -0 when `x` is a negative integer - const nz_frac = modf(-1000.0, iptr); + const nz_frac = f(@as(T, -1000.0), iptr); try expect(math.isNegativeZero(nz_frac)); - try expectEqual(-1000.0, iptr.*); + try expectEqual(@as(T, -1000.0), iptr.*); // Return +0 when `x` is a positive integer - const pz_frac = modf(1000.0, iptr); + const pz_frac = f(@as(T, 1000.0), iptr); try expect(math.isPositiveZero(pz_frac)); - try expectEqual(1000.0, iptr.*); + try expectEqual(@as(T, 1000.0), iptr.*); +} + +test "modf" { + try testModf(f64); + try testModf(f32); } fn nan(_: [*:0]const c_char) callconv(.c) f64 { diff --git a/lib/libc/mingw/math/modff.c b/lib/libc/mingw/math/modff.c deleted file mode 100644 index d2f8514d4d..0000000000 --- a/lib/libc/mingw/math/modff.c +++ /dev/null @@ -1,42 +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 -#include -#include - -float -modff (float value, float* iptr) -{ - float int_part = 0.0F; - /* truncate */ - /* truncate */ -#if (defined(_AMD64_) && !defined(_ARM64EC_)) || (defined(__x86_64__) && !defined(__arm64ec__)) - asm volatile ("subq $8, %%rsp\n" - "fnstcw 4(%%rsp)\n" - "movzwl 4(%%rsp), %%eax\n" - "orb $12, %%ah\n" - "movw %%ax, (%%rsp)\n" - "fldcw (%%rsp)\n" - "frndint\n" - "fldcw 4(%%rsp)\n" - "addq $8, %%rsp\n" : "=t" (int_part) : "0" (value) : "eax"); /* round */ -#elif defined(_X86_) || defined(__i386__) - asm volatile ("push %%eax\n\tsubl $8, %%esp\n" - "fnstcw 4(%%esp)\n" - "movzwl 4(%%esp), %%eax\n" - "orb $12, %%ah\n" - "movw %%ax, (%%esp)\n" - "fldcw (%%esp)\n" - "frndint\n" - "fldcw 4(%%esp)\n" - "addl $8, %%esp\n\tpop %%eax\n" : "=t" (int_part) : "0" (value) : "eax"); /* round */ -#else - int_part = truncf(value); -#endif - if (iptr) - *iptr = int_part; - return (isinf (value) ? 0.0F : value - int_part); -} diff --git a/lib/libc/musl/src/math/modff.c b/lib/libc/musl/src/math/modff.c deleted file mode 100644 index 639514effa..0000000000 --- a/lib/libc/musl/src/math/modff.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "libm.h" - -float modff(float x, float *iptr) -{ - union {float f; uint32_t i;} u = {x}; - uint32_t mask; - int e = (int)(u.i>>23 & 0xff) - 0x7f; - - /* no fractional part */ - if (e >= 23) { - *iptr = x; - if (e == 0x80 && u.i<<9 != 0) { /* nan */ - return x; - } - u.i &= 0x80000000; - return u.f; - } - /* no integral part */ - if (e < 0) { - u.i &= 0x80000000; - *iptr = u.f; - return x; - } - - mask = 0x007fffff>>e; - if ((u.i & mask) == 0) { - *iptr = x; - u.i &= 0x80000000; - return u.f; - } - u.i &= ~mask; - *iptr = u.f; - return x - u.f; -} diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig index 70e0d6e8c0..8168febe6d 100644 --- a/src/libs/mingw.zig +++ b/src/libs/mingw.zig @@ -977,7 +977,6 @@ const mingw32_x86_src = [_][]const u8{ const mingw32_x86_32_src = [_][]const u8{ // ucrtbase - "math" ++ path.sep_str ++ "modff.c", "math" ++ path.sep_str ++ "powf.c", "math" ++ path.sep_str ++ "sinhf.c", "math" ++ path.sep_str ++ "tanhf.c", diff --git a/src/libs/musl.zig b/src/libs/musl.zig index 3db43a1c76..ee7f63bf56 100644 --- a/src/libs/musl.zig +++ b/src/libs/musl.zig @@ -934,7 +934,6 @@ const src_files = [_][]const u8{ "musl/src/math/__math_uflowf.c", "musl/src/math/__math_xflow.c", "musl/src/math/__math_xflowf.c", - "musl/src/math/modff.c", "musl/src/math/modfl.c", "musl/src/math/nearbyint.c", "musl/src/math/nearbyintf.c", diff --git a/src/libs/wasi_libc.zig b/src/libs/wasi_libc.zig index 5845b4784c..3077aad385 100644 --- a/src/libs/wasi_libc.zig +++ b/src/libs/wasi_libc.zig @@ -755,7 +755,6 @@ const libc_top_half_src_files = [_][]const u8{ "musl/src/math/__math_uflowf.c", "musl/src/math/__math_xflow.c", "musl/src/math/__math_xflowf.c", - "musl/src/math/modff.c", "musl/src/math/modfl.c", "musl/src/math/nearbyintl.c", "musl/src/math/nextafter.c",