authorgravatar for hi@mihaelm.commihael <hi@mihaelm.com> 2026-04-27 16:48:55+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-27 16:48:55+02:00
log764760df62c63bc2a3d4bd4ec95c000233625834
tree03dc9eceaa5292c37f9ae65161b5e713f3c69a92
parent1deb029a665399838ca0bfbc451af02bc091200f

`libzigc/math`: Implement `rintl`, `lrintl` (#31791)

It's a fairly straightforward port of `musl`'s `rintl`, like `rint` and `rintf` were. `libc-test` tests for `rintl` are uncommented since they're now passing. I've also covered special cases for `rint` with tests, and broke down the current `rint` and `modf` test declarations into multiple -- so each libc function get its own test declaration at the very least. Contributes to #30978 Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31791 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

25 files changed, 195 insertions(+), 250 deletions(-)

lib/c/math.zig+31-2
...@@ -2,6 +2,7 @@ const builtin = @import("builtin");...@@ -2,6 +2,7 @@ const builtin = @import("builtin");
22
3const std = @import("std");3const std = @import("std");
4const math = std.math;4const math = std.math;
5const ld = math.long_double;
56
6const symbol = @import("../c.zig").symbol;7const symbol = @import("../c.zig").symbol;
78
...@@ -35,7 +36,9 @@ comptime {...@@ -35,7 +36,9 @@ comptime {
35 symbol(&frexpl, "frexpl");36 symbol(&frexpl, "frexpl");
36 symbol(&hypotf, "hypotf");37 symbol(&hypotf, "hypotf");
37 symbol(&hypotl, "hypotl");38 symbol(&hypotl, "hypotl");
39 symbol(&lrintl, "lrintl");
38 symbol(&modfl, "modfl");40 symbol(&modfl, "modfl");
41 symbol(&rintl, "rintl");
39 }42 }
4043
41 if ((builtin.target.isMinGW() and @sizeOf(f64) != @sizeOf(c_longdouble)) or builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) {44 if ((builtin.target.isMinGW() and @sizeOf(f64) != @sizeOf(c_longdouble)) or builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) {
...@@ -254,11 +257,15 @@ fn isnanl(x: c_longdouble) callconv(.c) c_int {...@@ -254,11 +257,15 @@ fn isnanl(x: c_longdouble) callconv(.c) c_int {
254}257}
255258
256fn lrint(x: f64) callconv(.c) c_long {259fn lrint(x: f64) callconv(.c) c_long {
257 return @intFromFloat(rint(x));260 return @trunc(rint(x));
258}261}
259262
260fn lrintf(x: f32) callconv(.c) c_long {263fn lrintf(x: f32) callconv(.c) c_long {
261 return @intFromFloat(rintf(x));264 return @trunc(rintf(x));
265}
266
267fn lrintl(x: c_longdouble) callconv(.c) c_long {
268 return @trunc(rintl(x));
262}269}
263270
264fn modfGeneric(comptime T: type, x: T, iptr: *T) T {271fn modfGeneric(comptime T: type, x: T, iptr: *T) T {
...@@ -364,6 +371,28 @@ fn rintf(x: f32) callconv(.c) f32 {...@@ -364,6 +371,28 @@ fn rintf(x: f32) callconv(.c) f32 {
364 return y;371 return y;
365}372}
366373
374fn rintl(x: c_longdouble) callconv(.c) c_longdouble {
375 if (@typeInfo(c_longdouble).float.bits == 64)
376 return rint(x);
377
378 const toint: c_longdouble = 1 << math.floatFractionalBits(c_longdouble);
379 const se = ld.signExponent(x);
380
381 if (se & 0x7fff >= 0x3fff + math.floatFractionalBits(c_longdouble))
382 return x;
383
384 var y: c_longdouble = undefined;
385 if ((se >> 15) == 1) {
386 y = x - toint + toint;
387 } else {
388 y = x + toint - toint;
389 }
390
391 if (y == 0)
392 return 0 * x;
393 return y;
394}
395
367fn tanh(x: f64) callconv(.c) f64 {396fn tanh(x: f64) callconv(.c) f64 {
368 return math.tanh(x);397 return math.tanh(x);
369}398}
lib/compiler_rt/cos.zig+1-1
...@@ -7,6 +7,7 @@...@@ -7,6 +7,7 @@
77
8const std = @import("std");8const std = @import("std");
9const math = std.math;9const math = std.math;
10const ld = math.long_double;
10const mem = std.mem;11const mem = std.mem;
11const expect = std.testing.expect;12const expect = std.testing.expect;
12const expectApproxEqAbs = std.testing.expectApproxEqAbs;13const expectApproxEqAbs = std.testing.expectApproxEqAbs;
...@@ -17,7 +18,6 @@ const trig = @import("trig.zig");...@@ -17,7 +18,6 @@ const trig = @import("trig.zig");
17const rem_pio2 = @import("rem_pio2.zig").rem_pio2;18const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
18const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;19const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
19const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l;20const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l;
20const ld = @import("long_double.zig");
2121
22comptime {22comptime {
23 symbol(&cosh, "__cosh");23 symbol(&cosh, "__cosh");
lib/compiler_rt/long_double.zig deleted-37
...@@ -1,37 +0,0 @@
1//! Utilities for dealing with the `long double` type (`f80` or `f128`)
2
3const std = @import("std");
4
5pub const U80 = std.meta.Int(.unsigned, 80);
6
7/// Returns the sign + exponent bits of a `long double`
8pub fn signExponent(x: anytype) u16 {
9 const T = @TypeOf(x);
10 switch (T) {
11 f80 => {
12 const bits: U80 = @bitCast(x);
13 return @intCast(bits >> 64);
14 },
15 f128 => {
16 const bits: u128 = @bitCast(x);
17 return @intCast(bits >> 112);
18 },
19 else => @compileError("`signExponent` supports only `f80` and `f128`, got: " ++ @typeName(T)),
20 }
21}
22
23/// Takes the top 16 bits of a `long double`'s mantissa
24pub fn mantissaTop(x: anytype) u16 {
25 const T = @TypeOf(x);
26 switch (T) {
27 f80 => {
28 const bits: U80 = @bitCast(x);
29 return @intCast((bits >> 48) & 0xFFFF);
30 },
31 f128 => {
32 const bits: u128 = @bitCast(x);
33 return @intCast((bits >> 96) & 0xFFFF);
34 },
35 else => @compileError("`mantissaTop` supports only `f80` and `f128`, got: " ++ @typeName(T)),
36 }
37}
lib/compiler_rt/rem_pio2l.zig+1-1
...@@ -5,8 +5,8 @@...@@ -5,8 +5,8 @@
55
6const std = @import("std");6const std = @import("std");
7const math = std.math;7const math = std.math;
8const ld = math.long_double;
89
9const ld = @import("long_double.zig");
10const rem_pio2_large = @import("rem_pio2_large.zig").rem_pio2_large;10const rem_pio2_large = @import("rem_pio2_large.zig").rem_pio2_large;
1111
12pub fn rem_pio2l(comptime T: type, x: T, y: *[2]T) i32 {12pub fn rem_pio2l(comptime T: type, x: T, y: *[2]T) i32 {
lib/compiler_rt/sin.zig+1-1
...@@ -7,6 +7,7 @@...@@ -7,6 +7,7 @@
77
8const std = @import("std");8const std = @import("std");
9const math = std.math;9const math = std.math;
10const ld = math.long_double;
10const mem = std.mem;11const mem = std.mem;
11const expect = std.testing.expect;12const expect = std.testing.expect;
12const expectApproxEqAbs = std.testing.expectApproxEqAbs;13const expectApproxEqAbs = std.testing.expectApproxEqAbs;
...@@ -17,7 +18,6 @@ const trig = @import("trig.zig");...@@ -17,7 +18,6 @@ const trig = @import("trig.zig");
17const rem_pio2 = @import("rem_pio2.zig").rem_pio2;18const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
18const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;19const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
19const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l;20const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l;
20const ld = @import("long_double.zig");
2121
22comptime {22comptime {
23 symbol(&sinh, "__sinh");23 symbol(&sinh, "__sinh");
lib/compiler_rt/sincos.zig+1-1
...@@ -2,6 +2,7 @@ const std = @import("std");...@@ -2,6 +2,7 @@ const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const arch = builtin.cpu.arch;3const arch = builtin.cpu.arch;
4const math = std.math;4const math = std.math;
5const ld = math.long_double;
5const mem = std.mem;6const mem = std.mem;
6const expect = std.testing.expect;7const expect = std.testing.expect;
7const expectApproxEqAbs = std.testing.expectApproxEqAbs;8const expectApproxEqAbs = std.testing.expectApproxEqAbs;
...@@ -9,7 +10,6 @@ const trig = @import("trig.zig");...@@ -9,7 +10,6 @@ const trig = @import("trig.zig");
9const rem_pio2 = @import("rem_pio2.zig").rem_pio2;10const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
10const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;11const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
11const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l;12const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l;
12const ld = @import("long_double.zig");
13const compiler_rt = @import("../compiler_rt.zig");13const compiler_rt = @import("../compiler_rt.zig");
14const symbol = compiler_rt.symbol;14const symbol = compiler_rt.symbol;
1515
lib/compiler_rt/tan.zig+1-1
...@@ -9,6 +9,7 @@...@@ -9,6 +9,7 @@
9const std = @import("std");9const std = @import("std");
10const builtin = @import("builtin");10const builtin = @import("builtin");
11const math = std.math;11const math = std.math;
12const ld = math.long_double;
12const mem = std.mem;13const mem = std.mem;
13const expect = std.testing.expect;14const expect = std.testing.expect;
14const expectApproxEqAbs = std.testing.expectApproxEqAbs;15const expectApproxEqAbs = std.testing.expectApproxEqAbs;
...@@ -17,7 +18,6 @@ const kernel = @import("trig.zig");...@@ -17,7 +18,6 @@ const kernel = @import("trig.zig");
17const rem_pio2 = @import("rem_pio2.zig").rem_pio2;18const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
18const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;19const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
19const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l;20const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l;
20const ld = @import("long_double.zig");
2121
22const arch = builtin.cpu.arch;22const arch = builtin.cpu.arch;
23const compiler_rt = @import("../compiler_rt.zig");23const compiler_rt = @import("../compiler_rt.zig");
lib/libc/mingw/math/lrintl.c deleted-18
...@@ -1,18 +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 <math.h>
7
8long lrintl (long double x)
9{
10 long retval = 0l;
11#if __SIZEOF_LONG_DOUBLE__ == __SIZEOF_DOUBLE__
12 retval = lrint(x);
13#elif defined(_AMD64_) || defined(__x86_64__) || defined(_X86_) || defined(__i386__)
14 __asm__ __volatile__ ("fistpl %0" : "=m" (retval) : "t" (x) : "st");
15#endif
16 return retval;
17}
18
lib/libc/mingw/math/rintl.c deleted-16
...@@ -1,16 +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 <math.h>
7
8long double rintl (long double x) {
9 long double retval = 0.0L;
10#if __SIZEOF_LONG_DOUBLE__ == __SIZEOF_DOUBLE__
11 retval = rint(x);
12#elif defined(_AMD64_) || defined(__x86_64__) || defined(_X86_) || defined(__i386__)
13 __asm__ __volatile__ ("frndint;": "=t" (retval) : "0" (x));
14#endif
15 return retval;
16}
lib/libc/musl/src/math/i386/lrintl.c deleted-8
...@@ -1,8 +0,0 @@
1#include <math.h>
2
3long lrintl(long double x)
4{
5 long r;
6 __asm__ ("fistpl %0" : "=m"(r) : "t"(x) : "st");
7 return r;
8}
lib/libc/musl/src/math/i386/rintl.c deleted-7
...@@ -1,7 +0,0 @@
1#include <math.h>
2
3long double rintl(long double x)
4{
5 __asm__ ("frndint" : "+t"(x));
6 return x;
7}
lib/libc/musl/src/math/lrintl.c deleted-36
...@@ -1,36 +0,0 @@
1#include <limits.h>
2#include <fenv.h>
3#include "libm.h"
4
5
6#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
7long lrintl(long double x)
8{
9 return lrint(x);
10}
11#elif defined(FE_INEXACT)
12/*
13see comments in lrint.c
14
15Note that if LONG_MAX == 0x7fffffffffffffff && LDBL_MANT_DIG == 64
16then x == 2**63 - 0.5 is the only input that overflows and
17raises inexact (with tonearest or upward rounding mode)
18*/
19long lrintl(long double x)
20{
21 #pragma STDC FENV_ACCESS ON
22 int e;
23
24 e = fetestexcept(FE_INEXACT);
25 x = rintl(x);
26 if (!e && (x > LONG_MAX || x < LONG_MIN))
27 feclearexcept(FE_INEXACT);
28 /* conversion */
29 return x;
30}
31#else
32long lrintl(long double x)
33{
34 return rintl(x);
35}
36#endif
lib/libc/musl/src/math/rintl.c deleted-29
...@@ -1,29 +0,0 @@
1#include "libm.h"
2
3#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
4long double rintl(long double x)
5{
6 return rint(x);
7}
8#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
9
10static const long double toint = 1/LDBL_EPSILON;
11
12long double rintl(long double x)
13{
14 union ldshape u = {x};
15 int e = u.i.se & 0x7fff;
16 int s = u.i.se >> 15;
17 long double y;
18
19 if (e >= 0x3fff+LDBL_MANT_DIG-1)
20 return x;
21 if (s)
22 y = x - toint + toint;
23 else
24 y = x + toint - toint;
25 if (y == 0)
26 return 0*x;
27 return y;
28}
29#endif
lib/libc/musl/src/math/s390x/rintl.c deleted-15
...@@ -1,15 +0,0 @@
1#include <math.h>
2
3#if defined(__HTM__) || __ARCH__ >= 9
4
5long double rintl(long double x)
6{
7 __asm__ ("fixbr %0, 0, %1" : "=f"(x) : "f"(x));
8 return x;
9}
10
11#else
12
13#include "../rintl.c"
14
15#endif
lib/libc/musl/src/math/x32/lrintl.s deleted-7
...@@ -1,7 +0,0 @@
1.global lrintl
2.type lrintl,@function
3lrintl:
4 fldt 8(%esp)
5 fistpl 8(%esp)
6 movl 8(%esp),%eax
7 ret
lib/libc/musl/src/math/x32/rintl.s deleted-6
...@@ -1,6 +0,0 @@
1.global rintl
2.type rintl,@function
3rintl:
4 fldt 8(%esp)
5 frndint
6 ret
lib/libc/musl/src/math/x86_64/lrintl.c deleted-8
...@@ -1,8 +0,0 @@
1#include <math.h>
2
3long lrintl(long double x)
4{
5 long r;
6 __asm__ ("fistpll %0" : "=m"(r) : "t"(x) : "st");
7 return r;
8}
lib/libc/musl/src/math/x86_64/rintl.c deleted-7
...@@ -1,7 +0,0 @@
1#include <math.h>
2
3long double rintl(long double x)
4{
5 __asm__ ("frndint" : "+t"(x));
6 return x;
7}
lib/std/math.zig+1
...@@ -58,6 +58,7 @@ pub const floatMax = float.floatMax;...@@ -58,6 +58,7 @@ pub const floatMax = float.floatMax;
58pub const floatEps = float.floatEps;58pub const floatEps = float.floatEps;
59pub const floatEpsAt = float.floatEpsAt;59pub const floatEpsAt = float.floatEpsAt;
60pub const inf = float.inf;60pub const inf = float.inf;
61pub const long_double = float.long_double;
61pub const nan = float.nan;62pub const nan = float.nan;
62pub const snan = float.snan;63pub const snan = float.snan;
6364
lib/std/math/float.zig+62
...@@ -4,6 +4,68 @@ const assert = std.debug.assert;...@@ -4,6 +4,68 @@ const assert = std.debug.assert;
4const expect = std.testing.expect;4const expect = std.testing.expect;
5const expectEqual = std.testing.expectEqual;5const expectEqual = std.testing.expectEqual;
66
7/// A namespace for functions that deal with floats that provide greater than
8/// double precision (`f80`, `f128`, `c_longdouble`). Commonly referred to as
9/// `long double` in C.
10pub const long_double = struct {
11 const U80 = @Int(.unsigned, 80);
12
13 inline fn bitWidth(x: anytype) u16 {
14 const T = @TypeOf(x);
15 return switch (T) {
16 f80, f128, c_longdouble => @typeInfo(T).float.bits,
17 else => @compileError("Unsupported type: " ++ @typeName(T) ++ "\nPass a `f80`, `f128`, or `c_longdouble`."),
18 };
19 }
20
21 /// Returns the sign + exponent bits of a `long double`.
22 pub fn signExponent(x: anytype) u16 {
23 const bit_width = bitWidth(x);
24 switch (bit_width) {
25 80 => {
26 const bits: U80 = @bitCast(x);
27 return @intCast(bits >> 64);
28 },
29 128 => {
30 const bits: u128 = @bitCast(x);
31 return @intCast(bits >> 112);
32 },
33 // `c_longdouble` can have <80 bits on some targets, we want to error on that
34 else => @compileError(std.fmt.comptimePrint("`signExponent` supports floats of only `80` and `128` bit width, got bit width: {d}", .{bit_width})),
35 }
36 }
37
38 test "signExponent" {
39 try expectEqual(signExponent(@as(f80, -0.0)), 0x8000);
40 try expectEqual(signExponent(@as(f128, 0.0)), 0x0000);
41 try expectEqual(signExponent(@as(f128, 42.0)), 0x4004);
42 try expectEqual(signExponent(nan(c_longdouble)), 0x7FFF);
43 }
44
45 /// Takes the top 16 bits of a `long double`'s mantissa.
46 pub fn mantissaTop(x: anytype) u16 {
47 const bit_width = bitWidth(x);
48 switch (bit_width) {
49 80 => {
50 const bits: U80 = @bitCast(x);
51 return @intCast((bits >> 48) & 0xFFFF);
52 },
53 128 => {
54 const bits: u128 = @bitCast(x);
55 return @intCast((bits >> 96) & 0xFFFF);
56 },
57 // `c_longdouble` can have <80 bits on some targets, we want to error on that
58 else => @compileError(std.fmt.comptimePrint("`mantissaTop` supports floats of only `80` and `128` bit width, got bit width: {d}", .{bit_width})),
59 }
60 }
61
62 test "mantissaTop" {
63 try expectEqual(mantissaTop(@as(f80, -0.0)), 0x0000);
64 try expectEqual(mantissaTop(nan(f128)), 0x8000);
65 try expectEqual(mantissaTop(@as(f128, 42.0)), 0x5000);
66 }
67};
68
7pub fn FloatRepr(comptime Float: type) type {69pub fn FloatRepr(comptime Float: type) type {
8 const fractional_bits = floatFractionalBits(Float);70 const fractional_bits = floatFractionalBits(Float);
9 const exponent_bits = floatExponentBits(Float);71 const exponent_bits = floatExponentBits(Float);
src/libs/mingw.zig-2
...@@ -853,9 +853,7 @@ const mingw32_x86_src = [_][]const u8{...@@ -853,9 +853,7 @@ const mingw32_x86_src = [_][]const u8{
853 "math" ++ path.sep_str ++ "fmal.c",853 "math" ++ path.sep_str ++ "fmal.c",
854 "math" ++ path.sep_str ++ "llrintl.c",854 "math" ++ path.sep_str ++ "llrintl.c",
855 "math" ++ path.sep_str ++ "llroundl.c",855 "math" ++ path.sep_str ++ "llroundl.c",
856 "math" ++ path.sep_str ++ "lrintl.c",
857 "math" ++ path.sep_str ++ "lroundl.c",856 "math" ++ path.sep_str ++ "lroundl.c",
858 "math" ++ path.sep_str ++ "rintl.c",
859 "math" ++ path.sep_str ++ "tgammal.c",857 "math" ++ path.sep_str ++ "tgammal.c",
860 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "_chgsignl.S",858 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "_chgsignl.S",
861 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acoshl.c",859 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acoshl.c",
src/libs/musl.zig-9
...@@ -850,14 +850,12 @@ const src_files = [_][]const u8{...@@ -850,14 +850,12 @@ const src_files = [_][]const u8{
850 "musl/src/math/i386/log1p.s",850 "musl/src/math/i386/log1p.s",
851 "musl/src/math/i386/log2l.s",851 "musl/src/math/i386/log2l.s",
852 "musl/src/math/i386/logl.s",852 "musl/src/math/i386/logl.s",
853 "musl/src/math/i386/lrintl.c",
854 "musl/src/math/i386/remainder.c",853 "musl/src/math/i386/remainder.c",
855 "musl/src/math/i386/remainderf.c",854 "musl/src/math/i386/remainderf.c",
856 "musl/src/math/i386/remainderl.c",855 "musl/src/math/i386/remainderl.c",
857 "musl/src/math/i386/remquof.s",856 "musl/src/math/i386/remquof.s",
858 "musl/src/math/i386/remquol.s",857 "musl/src/math/i386/remquol.s",
859 "musl/src/math/i386/remquo.s",858 "musl/src/math/i386/remquo.s",
860 "musl/src/math/i386/rintl.c",
861 "musl/src/math/i386/scalblnf.s",859 "musl/src/math/i386/scalblnf.s",
862 "musl/src/math/i386/scalblnl.s",860 "musl/src/math/i386/scalblnl.s",
863 "musl/src/math/i386/scalbln.s",861 "musl/src/math/i386/scalbln.s",
...@@ -897,7 +895,6 @@ const src_files = [_][]const u8{...@@ -897,7 +895,6 @@ const src_files = [_][]const u8{
897 "musl/src/math/logbf.c",895 "musl/src/math/logbf.c",
898 "musl/src/math/logbl.c",896 "musl/src/math/logbl.c",
899 "musl/src/math/logl.c",897 "musl/src/math/logl.c",
900 "musl/src/math/lrintl.c",
901 "musl/src/math/lround.c",898 "musl/src/math/lround.c",
902 "musl/src/math/lroundf.c",899 "musl/src/math/lroundf.c",
903 "musl/src/math/lroundl.c",900 "musl/src/math/lroundl.c",
...@@ -943,7 +940,6 @@ const src_files = [_][]const u8{...@@ -943,7 +940,6 @@ const src_files = [_][]const u8{
943 "musl/src/math/remquo.c",940 "musl/src/math/remquo.c",
944 "musl/src/math/remquof.c",941 "musl/src/math/remquof.c",
945 "musl/src/math/remquol.c",942 "musl/src/math/remquol.c",
946 "musl/src/math/rintl.c",
947 "musl/src/math/riscv32/fma.c",943 "musl/src/math/riscv32/fma.c",
948 "musl/src/math/riscv32/fmaf.c",944 "musl/src/math/riscv32/fmaf.c",
949 "musl/src/math/riscv64/fma.c",945 "musl/src/math/riscv64/fma.c",
...@@ -953,7 +949,6 @@ const src_files = [_][]const u8{...@@ -953,7 +949,6 @@ const src_files = [_][]const u8{
953 "musl/src/math/s390x/nearbyint.c",949 "musl/src/math/s390x/nearbyint.c",
954 "musl/src/math/s390x/nearbyintf.c",950 "musl/src/math/s390x/nearbyintf.c",
955 "musl/src/math/s390x/nearbyintl.c",951 "musl/src/math/s390x/nearbyintl.c",
956 "musl/src/math/s390x/rintl.c",
957 "musl/src/math/scalb.c",952 "musl/src/math/scalb.c",
958 "musl/src/math/scalbf.c",953 "musl/src/math/scalbf.c",
959 "musl/src/math/scalbln.c",954 "musl/src/math/scalbln.c",
...@@ -995,9 +990,7 @@ const src_files = [_][]const u8{...@@ -995,9 +990,7 @@ const src_files = [_][]const u8{
995 "musl/src/math/x32/log1pl.s",990 "musl/src/math/x32/log1pl.s",
996 "musl/src/math/x32/log2l.s",991 "musl/src/math/x32/log2l.s",
997 "musl/src/math/x32/logl.s",992 "musl/src/math/x32/logl.s",
998 "musl/src/math/x32/lrintl.s",
999 "musl/src/math/x32/remainderl.s",993 "musl/src/math/x32/remainderl.s",
1000 "musl/src/math/x32/rintl.s",
1001 "musl/src/math/x86_64/acosl.s",994 "musl/src/math/x86_64/acosl.s",
1002 "musl/src/math/x86_64/asinl.s",995 "musl/src/math/x86_64/asinl.s",
1003 "musl/src/math/x86_64/atan2l.s",996 "musl/src/math/x86_64/atan2l.s",
...@@ -1014,10 +1007,8 @@ const src_files = [_][]const u8{...@@ -1014,10 +1007,8 @@ const src_files = [_][]const u8{
1014 "musl/src/math/x86_64/log1pl.s",1007 "musl/src/math/x86_64/log1pl.s",
1015 "musl/src/math/x86_64/log2l.s",1008 "musl/src/math/x86_64/log2l.s",
1016 "musl/src/math/x86_64/logl.s",1009 "musl/src/math/x86_64/logl.s",
1017 "musl/src/math/x86_64/lrintl.c",
1018 "musl/src/math/x86_64/remainderl.c",1010 "musl/src/math/x86_64/remainderl.c",
1019 "musl/src/math/x86_64/remquol.c",1011 "musl/src/math/x86_64/remquol.c",
1020 "musl/src/math/x86_64/rintl.c",
1021 "musl/src/misc/a64l.c",1012 "musl/src/misc/a64l.c",
1022 "musl/src/misc/basename.c",1013 "musl/src/misc/basename.c",
1023 "musl/src/misc/dirname.c",1014 "musl/src/misc/dirname.c",
src/libs/wasi_libc.zig-2
...@@ -727,7 +727,6 @@ const libc_top_half_src_files = [_][]const u8{...@@ -727,7 +727,6 @@ const libc_top_half_src_files = [_][]const u8{
727 "musl/src/math/logbf.c",727 "musl/src/math/logbf.c",
728 "musl/src/math/logbl.c",728 "musl/src/math/logbl.c",
729 "musl/src/math/logl.c",729 "musl/src/math/logl.c",
730 "musl/src/math/lrintl.c",
731 "musl/src/math/lround.c",730 "musl/src/math/lround.c",
732 "musl/src/math/lroundf.c",731 "musl/src/math/lroundf.c",
733 "musl/src/math/lroundl.c",732 "musl/src/math/lroundl.c",
...@@ -761,7 +760,6 @@ const libc_top_half_src_files = [_][]const u8{...@@ -761,7 +760,6 @@ const libc_top_half_src_files = [_][]const u8{
761 "musl/src/math/remquo.c",760 "musl/src/math/remquo.c",
762 "musl/src/math/remquof.c",761 "musl/src/math/remquof.c",
763 "musl/src/math/remquol.c",762 "musl/src/math/remquol.c",
764 "musl/src/math/rintl.c",
765 "musl/src/math/scalb.c",763 "musl/src/math/scalb.c",
766 "musl/src/math/scalbf.c",764 "musl/src/math/scalbf.c",
767 "musl/src/math/scalbln.c",765 "musl/src/math/scalbln.c",
test/c/math.zig+95-35
...@@ -3,14 +3,19 @@ const std = @import("std");...@@ -3,14 +3,19 @@ const std = @import("std");
33
4const c = std.c;4const c = std.c;
5const math = std.math;5const math = std.math;
6
6const testing = std.testing;7const testing = std.testing;
8const expect = testing.expect;
9const expectEqual = testing.expectEqual;
10const expectApproxEqAbs = testing.expectApproxEqAbs;
11const expectApproxEqRel = testing.expectApproxEqRel;
712
8fn testModf(comptime T: type) !void {13fn testModf(comptime T: type) !void {
9 const f = switch (T) {14 const f = switch (T) {
10 f32 => c.modff,15 f32 => c.modff,
11 f64 => c.modf,16 f64 => c.modf,
12 c_longdouble => c.modfl,17 c_longdouble => c.modfl,
13 else => unreachable,18 else => @compileError("modf not implemented for " ++ @typeName(T)),
14 };19 };
1520
16 var int: T = undefined;21 var int: T = undefined;
...@@ -20,85 +25,140 @@ fn testModf(comptime T: type) !void {...@@ -20,85 +25,140 @@ fn testModf(comptime T: type) !void {
20 const normal_frac = f(@as(T, 1234.567), iptr);25 const normal_frac = f(@as(T, 1234.567), iptr);
21 // Account for precision error26 // Account for precision error
22 const expected = 1234.567 - @as(T, 1234);27 const expected = 1234.567 - @as(T, 1234);
23 try testing.expectApproxEqAbs(expected, normal_frac, eps_val);28 try expectApproxEqAbs(expected, normal_frac, eps_val);
24 try testing.expectApproxEqRel(@as(T, 1234.0), iptr.*, eps_val);29 try expectApproxEqRel(@as(T, 1234.0), iptr.*, eps_val);
2530
26 // When `x` is a NaN, NaN is returned and `*iptr` is set to NaN31 // When `x` is a NaN, NaN is returned and `*iptr` is set to NaN
27 const nan_frac = f(math.nan(T), iptr);32 const nan_frac = f(math.nan(T), iptr);
28 try testing.expect(math.isNan(nan_frac));33 try expect(math.isNan(nan_frac));
29 try testing.expect(math.isNan(iptr.*));34 try expect(math.isNan(iptr.*));
3035
31 // When `x` is positive infinity, +0 is returned and `*iptr` is set to36 // When `x` is positive infinity, +0 is returned and `*iptr` is set to
32 // positive infinity37 // positive infinity
33 const pos_zero_frac = f(math.inf(T), iptr);38 const pos_zero_frac = f(math.inf(T), iptr);
34 try testing.expect(math.isPositiveZero(pos_zero_frac));39 try expectEqual(0.0, pos_zero_frac);
35 try testing.expect(math.isPositiveInf(iptr.*));40 try expect(math.isPositiveInf(iptr.*));
3641
37 // When `x` is negative infinity, -0 is returned and `*iptr` is set to42 // When `x` is negative infinity, -0 is returned and `*iptr` is set to
38 // negative infinity43 // negative infinity
39 const neg_zero_frac = f(-math.inf(T), iptr);44 const neg_zero_frac = f(-math.inf(T), iptr);
40 try testing.expect(math.isNegativeZero(neg_zero_frac));45 try expectEqual(-0.0, neg_zero_frac);
41 try testing.expect(math.isNegativeInf(iptr.*));46 try expect(math.isNegativeInf(iptr.*));
4247
43 // Return -0 when `x` is a negative integer48 // Return -0 when `x` is a negative integer
44 const nz_frac = f(@as(T, -1000.0), iptr);49 const nz_frac = f(@as(T, -1000.0), iptr);
45 try testing.expect(math.isNegativeZero(nz_frac));50 try expectEqual(-0.0, nz_frac);
46 try testing.expectEqual(@as(T, -1000.0), iptr.*);51 try expectEqual(@as(T, -1000.0), iptr.*);
4752
48 // Return +0 when `x` is a positive integer53 // Return +0 when `x` is a positive integer
49 const pz_frac = f(@as(T, 1000.0), iptr);54 const pz_frac = f(@as(T, 1000.0), iptr);
50 try testing.expect(math.isPositiveZero(pz_frac));55 try expectEqual(0.0, pz_frac);
51 try testing.expectEqual(@as(T, 1000.0), iptr.*);56 try expectEqual(@as(T, 1000.0), iptr.*);
52}57}
5358
54test "modf" {59test "modf" {
55 try testModf(f32);
56 try testModf(f64);60 try testModf(f64);
61}
5762
58 if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO63test "modff" {
64 try testModf(f32);
65}
66
67test "modfl" {
68 if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO: see https://codeberg.org/ziglang/zig/issues/30976
5969
60 try testModf(c_longdouble);70 try testModf(c_longdouble);
61}71}
6272
63fn testRint(comptime T: type) !void {73fn testRintSpecial(comptime T: type) !void {
74 const f = switch (T) {
75 f32 => c.rintf,
76 f64 => c.rint,
77 c_longdouble => c.rintl,
78 else => @compileError("rint not implemented for" ++ @typeName(T)),
79 };
80
81 // For the special cases, x itself should be returned
82 try expectEqual(0.0, f(0.0));
83 try expectEqual(-0.0, f(-0.0));
84 try expectEqual(math.inf(T), f(math.inf(T)));
85 try expectEqual(-math.inf(T), f(-math.inf(T)));
86 try expect(math.isNan(f(math.nan(T))));
87}
88
89fn testRintNormal(comptime T: type) !void {
64 const f = switch (T) {90 const f = switch (T) {
65 f32 => c.rintf,91 f32 => c.rintf,
66 f64 => c.rint,92 f64 => c.rint,
93 c_longdouble => c.rintl,
67 else => @compileError("rint not implemented for" ++ @typeName(T)),94 else => @compileError("rint not implemented for" ++ @typeName(T)),
68 };95 };
6996
70 // Positive numbers round correctly97 // Positive numbers round correctly
71 try testing.expectEqual(@as(T, 42.0), f(42.2));98 try expectEqual(@as(T, 42.0), f(42.2));
72 try testing.expectEqual(@as(T, 42.0), f(41.8));99 try expectEqual(@as(T, 42.0), f(41.8));
100 try expectEqual(@as(T, 16_777_216.0), f(16_777_215.6));
73101
74 // Negative numbers round correctly102 // Negative numbers round correctly
75 try testing.expectEqual(@as(T, -6.0), f(-5.9));103 try expectEqual(@as(T, -6.0), f(-5.9));
76 try testing.expectEqual(@as(T, -6.0), f(-6.1));104 try expectEqual(@as(T, -6.0), f(-6.1));
105 // TODO: negative `long double`s close to `-n.5` seem to round to `-n.5`
106 // instead of either `-n.0` or `-(n-1).0` on NetBSD. For example, this
107 // case would round to `-16_777_215.5`.
108 if (!(T == c_longdouble and builtin.target.os.tag == .netbsd)) {
109 try expectEqual(@as(T, -16_777_215.0), f(-16_777_215.4));
110 }
77111
78 // No rounding needed test112 // No rounding needed test
79 try testing.expectEqual(@as(T, 5.0), f(5.0));113 try expectEqual(@as(T, 5.0), f(5.0));
80 try testing.expectEqual(@as(T, -10.0), f(-10.0));114 try expectEqual(@as(T, -10.0), f(-10.0));
81 try testing.expectEqual(@as(T, 0.0), f(0.0));115 try expectEqual(@as(T, 0.0), f(0.0));
82116
83 // Very large numbers return unchanged117 // Very large numbers return unchanged
84 const large: T = 9007199254740992.0; // 2^53118 const large: T = 9007199254740992.0; // 2^53
85 try testing.expectEqual(large, f(large));119 try expectEqual(large, f(large));
86 try testing.expectEqual(-large, f(-large));120 try expectEqual(-large, f(-large));
87121
88 // Small positive numbers round to zero122 // Small positive numbers round to zero
89 const pos_result = f(0.3);123 try expectEqual(@as(T, 0.0), f(0.3));
90 try testing.expect(math.isPositiveZero(pos_result));
91124
92 // Small negative numbers round to negative zero125 // TODO: negative `long double`s close to `-n.5` seem to round to `-n.5`
93 const neg_result = f(-0.3);126 // instead of either `-n.0` or `-(n-1).0` on NetBSD. For example, this
94 try testing.expect(math.isNegativeZero(neg_result));127 // case would round to `-0.5`.
128 if (!(T == c_longdouble and builtin.target.os.tag == .netbsd)) {
129 // Small negative numbers round to negative zero
130 try expectEqual(@as(T, -0.0), f(-0.3));
131 }
95132
96 // Exact half rounds to nearest even (banker's rounding)133 // Exact half rounds to nearest even (banker's rounding)
97 try testing.expectEqual(@as(T, 2.0), f(2.5));134 try expectEqual(@as(T, 2.0), f(2.5));
98 try testing.expectEqual(@as(T, 4.0), f(3.5));135 try expectEqual(@as(T, 4.0), f(3.5));
136}
137
138test "rintf.special" {
139 try testRintSpecial(f32);
140}
141
142test "rintf.normal" {
143 try testRintNormal(f32);
144}
145
146test "rint.special" {
147 try testRintSpecial(f64);
99}148}
100149
101test "rint" {150test "rint.normal" {
102 try testRint(f32);151 try testRintNormal(f64);
103 try testRint(f64);152}
153
154test "rintl.special" {
155 if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO: see https://codeberg.org/ziglang/zig/issues/30976
156
157 try testRintSpecial(c_longdouble);
158}
159
160test "rintl.normal" {
161 if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO: see https://codeberg.org/ziglang/zig/issues/30976
162
163 try testRintNormal(c_longdouble);
104}164}
test/libc.zig+1-1
...@@ -295,7 +295,7 @@ pub fn addCases(cases: *tests.LibcContext) void {...@@ -295,7 +295,7 @@ pub fn addCases(cases: *tests.LibcContext) void {
295 cases.addLibcTestCase("math/remquol.c", true, .{});295 cases.addLibcTestCase("math/remquol.c", true, .{});
296 cases.addLibcTestCase("math/rint.c", true, .{});296 cases.addLibcTestCase("math/rint.c", true, .{});
297 cases.addLibcTestCase("math/rintf.c", true, .{});297 cases.addLibcTestCase("math/rintf.c", true, .{});
298 // cases.addLibcTestCase("math/rintl.c", true, .{});298 cases.addLibcTestCase("math/rintl.c", true, .{});
299 cases.addLibcTestCase("math/round.c", true, .{});299 cases.addLibcTestCase("math/round.c", true, .{});
300 cases.addLibcTestCase("math/roundf.c", true, .{});300 cases.addLibcTestCase("math/roundf.c", true, .{});
301 cases.addLibcTestCase("math/roundl.c", true, .{});301 cases.addLibcTestCase("math/roundl.c", true, .{});