zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit eab22ab2b88313f86158230ddcdcb22f28483d85 (tree)
parent c77103add76a320d1ce877216e26dddd390caadc
Author: mihael <hi@mihaelm.com>
Date:   Sat, 14 Mar 2026 20:47:49 +0100

`libzigc`: Implement `modf`

The behaviour regarding special cases differs between `libc` and Zig's
`stdlib` for `modf`, so the implementation couldn't be a straightforward
calling of `stdlib` function.

Other than the obvious documented differences, I also had problems with
the `INVALID` flag being raised while running `libc-test` suite on riscv
arch through qemu. The solution was to test if the argument is `NaN`,
and then return a quiet `NaN` if so.

Passing tests, that should include all the special cases to be wary of,
were also added.

Test results:
```
$ stage4/bin/zig build test-libc -Dlibc-test-path=<LIBC-TEST-PATH> -Dtest-filter=modf -fqemu -fwasmtime --summary line
Build Summary: 921/921 steps succeeded
```

Diffstat:
Mlib/c/math.zig | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dlib/libc/musl/src/math/modf.c | 34----------------------------------
Msrc/libs/musl.zig | 1-
Msrc/libs/wasi_libc.zig | 1-
4 files changed, 63 insertions(+), 36 deletions(-)

diff --git a/lib/c/math.zig b/lib/c/math.zig @@ -53,6 +53,7 @@ comptime { symbol(&exp10, "exp10"); symbol(&exp10f, "exp10f"); symbol(&hypot, "hypot"); + symbol(&modf, "modf"); symbol(&pow, "pow"); symbol(&pow10, "pow10"); symbol(&pow10f, "pow10f"); @@ -162,6 +163,68 @@ 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 { + if (math.isNegativeInf(x)) { + iptr.* = -math.inf(f64); + return -0.0; + } + + if (math.isPositiveInf(x)) { + iptr.* = math.inf(f64); + return 0.0; + } + + // Avoids raising the INVALID flag on qemu-riscv + if (math.isNan(x)) { + iptr.* = math.nan(f64); + return math.nan(f64); + } + + const r = math.modf(x); + iptr.* = r.ipart; + + // If the result would be 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 = &int; + const eps_val = 1e-6; + + const normal_frac = modf(1234.5678, iptr); + try std.testing.expectApproxEqRel(0.5678, normal_frac, eps_val); + try std.testing.expectApproxEqRel(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); + try std.testing.expect(math.isNan(nan_frac)); + try std.testing.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); + try std.testing.expect(math.isPositiveZero(pos_zero_frac)); + try std.testing.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); + try std.testing.expect(math.isNegativeZero(neg_zero_frac)); + try std.testing.expect(math.isNegativeInf(iptr.*)); + + // Return -0 when `x` is a negative integer + const nz_frac = modf(-1000.0, iptr); + try std.testing.expect(math.isNegativeZero(nz_frac)); + try std.testing.expectEqual(-1000.0, iptr.*); + + // Return +0 when `x` is a positive integer + const pz_frac = modf(1000.0, iptr); + try std.testing.expect(math.isPositiveZero(pz_frac)); + try std.testing.expectEqual(1000.0, iptr.*); +} + fn nan(_: [*:0]const c_char) callconv(.c) f64 { return math.nan(f64); } diff --git a/lib/libc/musl/src/math/modf.c b/lib/libc/musl/src/math/modf.c @@ -1,34 +0,0 @@ -#include "libm.h" - -double modf(double x, double *iptr) -{ - union {double f; uint64_t i;} u = {x}; - uint64_t mask; - int e = (int)(u.i>>52 & 0x7ff) - 0x3ff; - - /* no fractional part */ - if (e >= 52) { - *iptr = x; - if (e == 0x400 && u.i<<12 != 0) /* nan */ - return x; - u.i &= 1ULL<<63; - return u.f; - } - - /* no integral part*/ - if (e < 0) { - u.i &= 1ULL<<63; - *iptr = u.f; - return x; - } - - mask = -1ULL>>12>>e; - if ((u.i & mask) == 0) { - *iptr = x; - u.i &= 1ULL<<63; - return u.f; - } - u.i &= ~mask; - *iptr = u.f; - return x - u.f; -} diff --git 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/modf.c", "musl/src/math/modff.c", "musl/src/math/modfl.c", "musl/src/math/nearbyint.c", diff --git 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/modf.c", "musl/src/math/modff.c", "musl/src/math/modfl.c", "musl/src/math/nearbyintl.c",