authorgravatar for hi@mihaelm.commihael <hi@mihaelm.com> 2026-03-15 20:24:28+01:00
committergravatar for hi@mihaelm.commihael <hi@mihaelm.com> 2026-03-17 23:33:10+01:00
log014178725744eda46db04ccfe44187ef616ceaf7
tree10d6df425c72dfd8f0d8cde54bbdd237ee648568
parent1c280032aae151b184e9224e2e0d1414f7436a01
signaturebadge-check Signed by SSH key SHA256:aoFoShdYLdrqMichqKXSSieTKUfACUIDJHsKc4V2tQg

`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=<LIBC-TEST-PATH> -Dtest-filter=modf -fqemu -fwasmtime --summary line Build Summary: 921/921 steps succeeded ``` ``` stage4/bin/zig build test-libc -Dlibc-test-path=<LIBC-TEST-PATH> -Dtest-filter=modff -fqemu -fwasmtime --summary line Build Summary: 369/369 steps succeeded ```

6 files changed, 43 insertions(+), 98 deletions(-)

lib/c/math.zig+43-19
...@@ -4,6 +4,7 @@ const std = @import("std");...@@ -4,6 +4,7 @@ const std = @import("std");
4const math = std.math;4const math = std.math;
5const expect = std.testing.expect;5const expect = std.testing.expect;
6const expectEqual = std.testing.expectEqual;6const expectEqual = std.testing.expectEqual;
7const expectApproxEqAbs = std.testing.expectApproxEqAbs;
7const expectApproxEqRel = std.testing.expectApproxEqRel;8const expectApproxEqRel = std.testing.expectApproxEqRel;
89
9const symbol = @import("../c.zig").symbol;10const symbol = @import("../c.zig").symbol;
...@@ -37,6 +38,7 @@ comptime {...@@ -37,6 +38,7 @@ comptime {
37 symbol(&coshf, "coshf");38 symbol(&coshf, "coshf");
38 symbol(&hypotf, "hypotf");39 symbol(&hypotf, "hypotf");
39 symbol(&hypotl, "hypotl");40 symbol(&hypotl, "hypotl");
41 symbol(&modff, "modff");
40 symbol(&nan, "nan");42 symbol(&nan, "nan");
41 symbol(&nanf, "nanf");43 symbol(&nanf, "nanf");
42 symbol(&nanl, "nanl");44 symbol(&nanl, "nanl");
...@@ -166,66 +168,88 @@ fn isnanl(x: c_longdouble) callconv(.c) c_int {...@@ -166,66 +168,88 @@ fn isnanl(x: c_longdouble) callconv(.c) c_int {
166 return if (math.isNan(x)) 1 else 0;168 return if (math.isNan(x)) 1 else 0;
167}169}
168170
169fn modf(x: f64, iptr: *f64) callconv(.c) f64 {171fn modfGeneric(comptime T: type, x: T, iptr: *T) T {
170 if (math.isNegativeInf(x)) {172 if (math.isNegativeInf(x)) {
171 iptr.* = -math.inf(f64);173 iptr.* = -math.inf(T);
172 return -0.0;174 return -0.0;
173 }175 }
174176
175 if (math.isPositiveInf(x)) {177 if (math.isPositiveInf(x)) {
176 iptr.* = math.inf(f64);178 iptr.* = math.inf(T);
177 return 0.0;179 return 0.0;
178 }180 }
179181
180 // Avoids raising the INVALID flag on qemu-riscv182 // Avoids raising the INVALID flag on qemu-riscv
181 if (math.isNan(x)) {183 if (math.isNan(x)) {
182 iptr.* = math.nan(f64);184 iptr.* = math.nan(T);
183 return math.nan(f64);185 return math.nan(T);
184 }186 }
185187
186 const r = math.modf(x);188 const r = math.modf(x);
187 iptr.* = r.ipart;189 iptr.* = r.ipart;
188190
189 // If the result would be a negative zero, we must be explicit about191 // If the result is a negative zero, we must be explicit about
190 // returning a negative zero.192 // returning a negative zero.
191 return if (math.isNegativeZero(x) or (x < 0.0 and x == r.ipart)) -0.0 else r.fpart;193 return if (math.isNegativeZero(x) or (x < 0.0 and x == r.ipart)) -0.0 else r.fpart;
192}194}
193195
194test "modf" {196fn modf(x: f64, iptr: *f64) callconv(.c) f64 {
195 var int: f64 = undefined;197 return modfGeneric(f64, x, iptr);
198}
199
200fn modff(x: f32, iptr: *f32) callconv(.c) f32 {
201 return modfGeneric(f32, x, iptr);
202}
203
204fn testModf(comptime T: type) !void {
205 // Choose the appropriate `modf` impl to test based on type
206 const f = switch (T) {
207 f64 => modf,
208 f32 => modff,
209 else => @compileError("modf not implemented for " ++ @typeName(T)),
210 };
211
212 var int: T = undefined;
196 const iptr = &int;213 const iptr = &int;
197 const eps_val = 1e-6;214 const eps_val: comptime_float = @max(1e-6, math.floatEps(T));
198215
199 const normal_frac = modf(1234.5678, iptr);216 const normal_frac = f(@as(T, 1234.567), iptr);
200 try expectApproxEqRel(0.5678, normal_frac, eps_val);217 // Account for precision error
201 try expectApproxEqRel(1234.0, iptr.*, eps_val);218 const expected = 1234.567 - @as(T, 1234);
219 try expectApproxEqAbs(expected, normal_frac, eps_val);
220 try expectApproxEqRel(@as(T, 1234.0), iptr.*, eps_val);
202221
203 // When `x` is a NaN, NaN is returned and `*iptr` is set to NaN222 // When `x` is a NaN, NaN is returned and `*iptr` is set to NaN
204 const nan_frac = modf(math.nan(f64), iptr);223 const nan_frac = f(math.nan(T), iptr);
205 try expect(math.isNan(nan_frac));224 try expect(math.isNan(nan_frac));
206 try expect(math.isNan(iptr.*));225 try expect(math.isNan(iptr.*));
207226
208 // When `x` is positive infinity, +0 is returned and `*iptr` is set to227 // When `x` is positive infinity, +0 is returned and `*iptr` is set to
209 // positive infinity228 // positive infinity
210 const pos_zero_frac = modf(math.inf(f64), iptr);229 const pos_zero_frac = f(math.inf(T), iptr);
211 try expect(math.isPositiveZero(pos_zero_frac));230 try expect(math.isPositiveZero(pos_zero_frac));
212 try expect(math.isPositiveInf(iptr.*));231 try expect(math.isPositiveInf(iptr.*));
213232
214 // When `x` is negative infinity, -0 is returned and `*iptr` is set to233 // When `x` is negative infinity, -0 is returned and `*iptr` is set to
215 // negative infinity234 // negative infinity
216 const neg_zero_frac = modf(-math.inf(f64), iptr);235 const neg_zero_frac = f(-math.inf(T), iptr);
217 try expect(math.isNegativeZero(neg_zero_frac));236 try expect(math.isNegativeZero(neg_zero_frac));
218 try expect(math.isNegativeInf(iptr.*));237 try expect(math.isNegativeInf(iptr.*));
219238
220 // Return -0 when `x` is a negative integer239 // Return -0 when `x` is a negative integer
221 const nz_frac = modf(-1000.0, iptr);240 const nz_frac = f(@as(T, -1000.0), iptr);
222 try expect(math.isNegativeZero(nz_frac));241 try expect(math.isNegativeZero(nz_frac));
223 try expectEqual(-1000.0, iptr.*);242 try expectEqual(@as(T, -1000.0), iptr.*);
224243
225 // Return +0 when `x` is a positive integer244 // Return +0 when `x` is a positive integer
226 const pz_frac = modf(1000.0, iptr);245 const pz_frac = f(@as(T, 1000.0), iptr);
227 try expect(math.isPositiveZero(pz_frac));246 try expect(math.isPositiveZero(pz_frac));
228 try expectEqual(1000.0, iptr.*);247 try expectEqual(@as(T, 1000.0), iptr.*);
248}
249
250test "modf" {
251 try testModf(f64);
252 try testModf(f32);
229}253}
230254
231fn nan(_: [*:0]const c_char) callconv(.c) f64 {255fn nan(_: [*:0]const c_char) callconv(.c) f64 {
lib/libc/mingw/math/modff.c deleted-42
...@@ -1,42 +0,0 @@
1/**
2 * This file has no copyright assigned and is placed in the Public Domain.
3 * This file is part of the mingw-w64 runtime package.
4 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
5 */
6#include <fenv.h>
7#include <math.h>
8#include <errno.h>
9
10float
11modff (float value, float* iptr)
12{
13 float int_part = 0.0F;
14 /* truncate */
15 /* truncate */
16#if (defined(_AMD64_) && !defined(_ARM64EC_)) || (defined(__x86_64__) && !defined(__arm64ec__))
17 asm volatile ("subq $8, %%rsp\n"
18 "fnstcw 4(%%rsp)\n"
19 "movzwl 4(%%rsp), %%eax\n"
20 "orb $12, %%ah\n"
21 "movw %%ax, (%%rsp)\n"
22 "fldcw (%%rsp)\n"
23 "frndint\n"
24 "fldcw 4(%%rsp)\n"
25 "addq $8, %%rsp\n" : "=t" (int_part) : "0" (value) : "eax"); /* round */
26#elif defined(_X86_) || defined(__i386__)
27 asm volatile ("push %%eax\n\tsubl $8, %%esp\n"
28 "fnstcw 4(%%esp)\n"
29 "movzwl 4(%%esp), %%eax\n"
30 "orb $12, %%ah\n"
31 "movw %%ax, (%%esp)\n"
32 "fldcw (%%esp)\n"
33 "frndint\n"
34 "fldcw 4(%%esp)\n"
35 "addl $8, %%esp\n\tpop %%eax\n" : "=t" (int_part) : "0" (value) : "eax"); /* round */
36#else
37 int_part = truncf(value);
38#endif
39 if (iptr)
40 *iptr = int_part;
41 return (isinf (value) ? 0.0F : value - int_part);
42}
lib/libc/musl/src/math/modff.c deleted-34
...@@ -1,34 +0,0 @@
1#include "libm.h"
2
3float modff(float x, float *iptr)
4{
5 union {float f; uint32_t i;} u = {x};
6 uint32_t mask;
7 int e = (int)(u.i>>23 & 0xff) - 0x7f;
8
9 /* no fractional part */
10 if (e >= 23) {
11 *iptr = x;
12 if (e == 0x80 && u.i<<9 != 0) { /* nan */
13 return x;
14 }
15 u.i &= 0x80000000;
16 return u.f;
17 }
18 /* no integral part */
19 if (e < 0) {
20 u.i &= 0x80000000;
21 *iptr = u.f;
22 return x;
23 }
24
25 mask = 0x007fffff>>e;
26 if ((u.i & mask) == 0) {
27 *iptr = x;
28 u.i &= 0x80000000;
29 return u.f;
30 }
31 u.i &= ~mask;
32 *iptr = u.f;
33 return x - u.f;
34}
src/libs/mingw.zig-1
...@@ -977,7 +977,6 @@ const mingw32_x86_src = [_][]const u8{...@@ -977,7 +977,6 @@ const mingw32_x86_src = [_][]const u8{
977977
978const mingw32_x86_32_src = [_][]const u8{978const mingw32_x86_32_src = [_][]const u8{
979 // ucrtbase979 // ucrtbase
980 "math" ++ path.sep_str ++ "modff.c",
981 "math" ++ path.sep_str ++ "powf.c",980 "math" ++ path.sep_str ++ "powf.c",
982 "math" ++ path.sep_str ++ "sinhf.c",981 "math" ++ path.sep_str ++ "sinhf.c",
983 "math" ++ path.sep_str ++ "tanhf.c",982 "math" ++ path.sep_str ++ "tanhf.c",
src/libs/musl.zig-1
...@@ -934,7 +934,6 @@ const src_files = [_][]const u8{...@@ -934,7 +934,6 @@ const src_files = [_][]const u8{
934 "musl/src/math/__math_uflowf.c",934 "musl/src/math/__math_uflowf.c",
935 "musl/src/math/__math_xflow.c",935 "musl/src/math/__math_xflow.c",
936 "musl/src/math/__math_xflowf.c",936 "musl/src/math/__math_xflowf.c",
937 "musl/src/math/modff.c",
938 "musl/src/math/modfl.c",937 "musl/src/math/modfl.c",
939 "musl/src/math/nearbyint.c",938 "musl/src/math/nearbyint.c",
940 "musl/src/math/nearbyintf.c",939 "musl/src/math/nearbyintf.c",
src/libs/wasi_libc.zig-1
...@@ -755,7 +755,6 @@ const libc_top_half_src_files = [_][]const u8{...@@ -755,7 +755,6 @@ const libc_top_half_src_files = [_][]const u8{
755 "musl/src/math/__math_uflowf.c",755 "musl/src/math/__math_uflowf.c",
756 "musl/src/math/__math_xflow.c",756 "musl/src/math/__math_xflow.c",
757 "musl/src/math/__math_xflowf.c",757 "musl/src/math/__math_xflowf.c",
758 "musl/src/math/modff.c",
759 "musl/src/math/modfl.c",758 "musl/src/math/modfl.c",
760 "musl/src/math/nearbyintl.c",759 "musl/src/math/nearbyintl.c",
761 "musl/src/math/nextafter.c",760 "musl/src/math/nextafter.c",