| author | |
| committer | |
| log | eab22ab2b88313f86158230ddcdcb22f28483d85 |
| tree | 41b9ecd57de092ff4f2951508f6a997436c5b3d1 |
| parent | c77103add76a320d1ce877216e26dddd390caadc |
| signature |
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
```4 files changed, 63 insertions(+), 36 deletions(-)
lib/c/math.zig+63| ... | @@ -53,6 +53,7 @@ comptime { | ... | @@ -53,6 +53,7 @@ comptime { |
| 53 | symbol(&exp10, "exp10"); | 53 | symbol(&exp10, "exp10"); |
| 54 | symbol(&exp10f, "exp10f"); | 54 | symbol(&exp10f, "exp10f"); |
| 55 | symbol(&hypot, "hypot"); | 55 | symbol(&hypot, "hypot"); |
| 56 | symbol(&modf, "modf"); | ||
| 56 | symbol(&pow, "pow"); | 57 | symbol(&pow, "pow"); |
| 57 | symbol(&pow10, "pow10"); | 58 | symbol(&pow10, "pow10"); |
| 58 | symbol(&pow10f, "pow10f"); | 59 | symbol(&pow10f, "pow10f"); |
| ... | @@ -162,6 +163,68 @@ fn isnanl(x: c_longdouble) callconv(.c) c_int { | ... | @@ -162,6 +163,68 @@ fn isnanl(x: c_longdouble) callconv(.c) c_int { |
| 162 | return if (math.isNan(x)) 1 else 0; | 163 | return if (math.isNan(x)) 1 else 0; |
| 163 | } | 164 | } |
| 164 | 165 | ||
| 166 | fn modf(x: f64, iptr: *f64) callconv(.c) f64 { | ||
| 167 | if (math.isNegativeInf(x)) { | ||
| 168 | iptr.* = -math.inf(f64); | ||
| 169 | return -0.0; | ||
| 170 | } | ||
| 171 | |||
| 172 | if (math.isPositiveInf(x)) { | ||
| 173 | iptr.* = math.inf(f64); | ||
| 174 | return 0.0; | ||
| 175 | } | ||
| 176 | |||
| 177 | // Avoids raising the INVALID flag on qemu-riscv | ||
| 178 | if (math.isNan(x)) { | ||
| 179 | iptr.* = math.nan(f64); | ||
| 180 | return math.nan(f64); | ||
| 181 | } | ||
| 182 | |||
| 183 | const r = math.modf(x); | ||
| 184 | iptr.* = r.ipart; | ||
| 185 | |||
| 186 | // If the result would be a negative zero, we must be explicit about | ||
| 187 | // returning a negative zero. | ||
| 188 | return if (math.isNegativeZero(x) or (x < 0.0 and x == r.ipart)) -0.0 else r.fpart; | ||
| 189 | } | ||
| 190 | |||
| 191 | test "modf" { | ||
| 192 | var int: f64 = undefined; | ||
| 193 | const iptr = &int; | ||
| 194 | const eps_val = 1e-6; | ||
| 195 | |||
| 196 | const normal_frac = modf(1234.5678, iptr); | ||
| 197 | try std.testing.expectApproxEqRel(0.5678, normal_frac, eps_val); | ||
| 198 | try std.testing.expectApproxEqRel(1234.0, iptr.*, eps_val); | ||
| 199 | |||
| 200 | // When `x` is a NaN, NaN is returned and `*iptr` is set to NaN | ||
| 201 | const nan_frac = modf(math.nan(f64), iptr); | ||
| 202 | try std.testing.expect(math.isNan(nan_frac)); | ||
| 203 | try std.testing.expect(math.isNan(iptr.*)); | ||
| 204 | |||
| 205 | // When `x` is positive infinity, +0 is returned and `*iptr` is set to | ||
| 206 | // positive infinity | ||
| 207 | const pos_zero_frac = modf(math.inf(f64), iptr); | ||
| 208 | try std.testing.expect(math.isPositiveZero(pos_zero_frac)); | ||
| 209 | try std.testing.expect(math.isPositiveInf(iptr.*)); | ||
| 210 | |||
| 211 | // When `x` is negative infinity, -0 is returned and `*iptr` is set to | ||
| 212 | // negative infinity | ||
| 213 | const neg_zero_frac = modf(-math.inf(f64), iptr); | ||
| 214 | try std.testing.expect(math.isNegativeZero(neg_zero_frac)); | ||
| 215 | try std.testing.expect(math.isNegativeInf(iptr.*)); | ||
| 216 | |||
| 217 | // Return -0 when `x` is a negative integer | ||
| 218 | const nz_frac = modf(-1000.0, iptr); | ||
| 219 | try std.testing.expect(math.isNegativeZero(nz_frac)); | ||
| 220 | try std.testing.expectEqual(-1000.0, iptr.*); | ||
| 221 | |||
| 222 | // Return +0 when `x` is a positive integer | ||
| 223 | const pz_frac = modf(1000.0, iptr); | ||
| 224 | try std.testing.expect(math.isPositiveZero(pz_frac)); | ||
| 225 | try std.testing.expectEqual(1000.0, iptr.*); | ||
| 226 | } | ||
| 227 | |||
| 165 | fn nan(_: [*:0]const c_char) callconv(.c) f64 { | 228 | fn nan(_: [*:0]const c_char) callconv(.c) f64 { |
| 166 | return math.nan(f64); | 229 | return math.nan(f64); |
| 167 | } | 230 | } |
lib/libc/musl/src/math/modf.c deleted-34| ... | @@ -1,34 +0,0 @@ | ||
| 1 | #include "libm.h" | ||
| 2 | |||
| 3 | double modf(double x, double *iptr) | ||
| 4 | { | ||
| 5 | 	union {double f; uint64_t i;} u = {x}; | ||
| 6 | 	uint64_t mask; | ||
| 7 | 	int e = (int)(u.i>>52 & 0x7ff) - 0x3ff; | ||
| 8 | |||
| 9 | 	/* no fractional part */ | ||
| 10 | 	if (e >= 52) { | ||
| 11 | 		*iptr = x; | ||
| 12 | 		if (e == 0x400 && u.i<<12 != 0) /* nan */ | ||
| 13 | 			return x; | ||
| 14 | 		u.i &= 1ULL<<63; | ||
| 15 | 		return u.f; | ||
| 16 | 	} | ||
| 17 | |||
| 18 | 	/* no integral part*/ | ||
| 19 | 	if (e < 0) { | ||
| 20 | 		u.i &= 1ULL<<63; | ||
| 21 | 		*iptr = u.f; | ||
| 22 | 		return x; | ||
| 23 | 	} | ||
| 24 | |||
| 25 | 	mask = -1ULL>>12>>e; | ||
| 26 | 	if ((u.i & mask) == 0) { | ||
| 27 | 		*iptr = x; | ||
| 28 | 		u.i &= 1ULL<<63; | ||
| 29 | 		return u.f; | ||
| 30 | 	} | ||
| 31 | 	u.i &= ~mask; | ||
| 32 | 	*iptr = u.f; | ||
| 33 | 	return x - u.f; | ||
| 34 | } | ||
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/modf.c", | ||
| 938 | "musl/src/math/modff.c", | 937 | "musl/src/math/modff.c", |
| 939 | "musl/src/math/modfl.c", | 938 | "musl/src/math/modfl.c", |
| 940 | "musl/src/math/nearbyint.c", | 939 | "musl/src/math/nearbyint.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/modf.c", | ||
| 759 | "musl/src/math/modff.c", | 758 | "musl/src/math/modff.c", |
| 760 | "musl/src/math/modfl.c", | 759 | "musl/src/math/modfl.c", |
| 761 | "musl/src/math/nearbyintl.c", | 760 | "musl/src/math/nearbyintl.c", |