| author | |
| committer | |
| log | 014178725744eda46db04ccfe44187ef616ceaf7 |
| tree | 10d6df425c72dfd8f0d8cde54bbdd237ee648568 |
| parent | 1c280032aae151b184e9224e2e0d1414f7436a01 |
| signature |
`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 | 4 | const math = std.math; |
| 5 | 5 | const expect = std.testing.expect; |
| 6 | 6 | const expectEqual = std.testing.expectEqual; |
| 7 | const expectApproxEqAbs = std.testing.expectApproxEqAbs; | |
| 7 | 8 | const expectApproxEqRel = std.testing.expectApproxEqRel; |
| 8 | 9 | |
| 9 | 10 | const symbol = @import("../c.zig").symbol; |
| ... | ... | @@ -37,6 +38,7 @@ comptime { |
| 37 | 38 | symbol(&coshf, "coshf"); |
| 38 | 39 | symbol(&hypotf, "hypotf"); |
| 39 | 40 | symbol(&hypotl, "hypotl"); |
| 41 | symbol(&modff, "modff"); | |
| 40 | 42 | symbol(&nan, "nan"); |
| 41 | 43 | symbol(&nanf, "nanf"); |
| 42 | 44 | symbol(&nanl, "nanl"); |
| ... | ... | @@ -166,66 +168,88 @@ fn isnanl(x: c_longdouble) callconv(.c) c_int { |
| 166 | 168 | return if (math.isNan(x)) 1 else 0; |
| 167 | 169 | } |
| 168 | 170 | |
| 169 | fn modf(x: f64, iptr: *f64) callconv(.c) f64 { | |
| 171 | fn modfGeneric(comptime T: type, x: T, iptr: *T) T { | |
| 170 | 172 | if (math.isNegativeInf(x)) { |
| 171 | iptr.* = -math.inf(f64); | |
| 173 | iptr.* = -math.inf(T); | |
| 172 | 174 | return -0.0; |
| 173 | 175 | } |
| 174 | 176 | |
| 175 | 177 | if (math.isPositiveInf(x)) { |
| 176 | iptr.* = math.inf(f64); | |
| 178 | iptr.* = math.inf(T); | |
| 177 | 179 | return 0.0; |
| 178 | 180 | } |
| 179 | 181 | |
| 180 | 182 | // Avoids raising the INVALID flag on qemu-riscv |
| 181 | 183 | if (math.isNan(x)) { |
| 182 | iptr.* = math.nan(f64); | |
| 183 | return math.nan(f64); | |
| 184 | iptr.* = math.nan(T); | |
| 185 | return math.nan(T); | |
| 184 | 186 | } |
| 185 | 187 | |
| 186 | 188 | const r = math.modf(x); |
| 187 | 189 | iptr.* = r.ipart; |
| 188 | 190 | |
| 189 | // If the result would be a negative zero, we must be explicit about | |
| 191 | // If the result is a negative zero, we must be explicit about | |
| 190 | 192 | // returning a negative zero. |
| 191 | 193 | return if (math.isNegativeZero(x) or (x < 0.0 and x == r.ipart)) -0.0 else r.fpart; |
| 192 | 194 | } |
| 193 | 195 | |
| 194 | test "modf" { | |
| 195 | var int: f64 = undefined; | |
| 196 | fn modf(x: f64, iptr: *f64) callconv(.c) f64 { | |
| 197 | return modfGeneric(f64, x, iptr); | |
| 198 | } | |
| 199 | ||
| 200 | fn modff(x: f32, iptr: *f32) callconv(.c) f32 { | |
| 201 | return modfGeneric(f32, x, iptr); | |
| 202 | } | |
| 203 | ||
| 204 | fn 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 | 213 | const iptr = &int; |
| 197 | const eps_val = 1e-6; | |
| 214 | const eps_val: comptime_float = @max(1e-6, math.floatEps(T)); | |
| 198 | 215 | |
| 199 | const normal_frac = modf(1234.5678, iptr); | |
| 200 | try expectApproxEqRel(0.5678, normal_frac, eps_val); | |
| 201 | try expectApproxEqRel(1234.0, iptr.*, eps_val); | |
| 216 | const normal_frac = f(@as(T, 1234.567), iptr); | |
| 217 | // Account for precision error | |
| 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); | |
| 202 | 221 | |
| 203 | 222 | // 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 | 224 | try expect(math.isNan(nan_frac)); |
| 206 | 225 | try expect(math.isNan(iptr.*)); |
| 207 | 226 | |
| 208 | 227 | // When `x` is positive infinity, +0 is returned and `*iptr` is set to |
| 209 | 228 | // positive infinity |
| 210 | const pos_zero_frac = modf(math.inf(f64), iptr); | |
| 229 | const pos_zero_frac = f(math.inf(T), iptr); | |
| 211 | 230 | try expect(math.isPositiveZero(pos_zero_frac)); |
| 212 | 231 | try expect(math.isPositiveInf(iptr.*)); |
| 213 | 232 | |
| 214 | 233 | // When `x` is negative infinity, -0 is returned and `*iptr` is set to |
| 215 | 234 | // negative infinity |
| 216 | const neg_zero_frac = modf(-math.inf(f64), iptr); | |
| 235 | const neg_zero_frac = f(-math.inf(T), iptr); | |
| 217 | 236 | try expect(math.isNegativeZero(neg_zero_frac)); |
| 218 | 237 | try expect(math.isNegativeInf(iptr.*)); |
| 219 | 238 | |
| 220 | 239 | // 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 | 241 | try expect(math.isNegativeZero(nz_frac)); |
| 223 | try expectEqual(-1000.0, iptr.*); | |
| 242 | try expectEqual(@as(T, -1000.0), iptr.*); | |
| 224 | 243 | |
| 225 | 244 | // 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 | 246 | try expect(math.isPositiveZero(pz_frac)); |
| 228 | try expectEqual(1000.0, iptr.*); | |
| 247 | try expectEqual(@as(T, 1000.0), iptr.*); | |
| 248 | } | |
| 249 | ||
| 250 | test "modf" { | |
| 251 | try testModf(f64); | |
| 252 | try testModf(f32); | |
| 229 | 253 | } |
| 230 | 254 | |
| 231 | 255 | fn 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 | ||
| 10 | float | |
| 11 | modff (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 | ||
| 3 | float 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 | 977 | |
| 978 | 978 | const mingw32_x86_32_src = [_][]const u8{ |
| 979 | 979 | // ucrtbase |
| 980 | "math" ++ path.sep_str ++ "modff.c", | |
| 981 | 980 | "math" ++ path.sep_str ++ "powf.c", |
| 982 | 981 | "math" ++ path.sep_str ++ "sinhf.c", |
| 983 | 982 | "math" ++ path.sep_str ++ "tanhf.c", |
src/libs/musl.zig-1| ... | ... | @@ -934,7 +934,6 @@ const src_files = [_][]const u8{ |
| 934 | 934 | "musl/src/math/__math_uflowf.c", |
| 935 | 935 | "musl/src/math/__math_xflow.c", |
| 936 | 936 | "musl/src/math/__math_xflowf.c", |
| 937 | "musl/src/math/modff.c", | |
| 938 | 937 | "musl/src/math/modfl.c", |
| 939 | 938 | "musl/src/math/nearbyint.c", |
| 940 | 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 | 755 | "musl/src/math/__math_uflowf.c", |
| 756 | 756 | "musl/src/math/__math_xflow.c", |
| 757 | 757 | "musl/src/math/__math_xflowf.c", |
| 758 | "musl/src/math/modff.c", | |
| 759 | 758 | "musl/src/math/modfl.c", |
| 760 | 759 | "musl/src/math/nearbyintl.c", |
| 761 | 760 | "musl/src/math/nextafter.c", |