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");
22
33const std = @import("std");
44const math = std.math;
5const ld = math.long_double;
56
67const symbol = @import("../c.zig").symbol;
78
......@@ -35,7 +36,9 @@ comptime {
3536 symbol(&frexpl, "frexpl");
3637 symbol(&hypotf, "hypotf");
3738 symbol(&hypotl, "hypotl");
39 symbol(&lrintl, "lrintl");
3840 symbol(&modfl, "modfl");
41 symbol(&rintl, "rintl");
3942 }
4043
4144 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 {
254257}
255258
256259fn lrint(x: f64) callconv(.c) c_long {
257 return @intFromFloat(rint(x));
260 return @trunc(rint(x));
258261}
259262
260263fn 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));
262269}
263270
264271fn modfGeneric(comptime T: type, x: T, iptr: *T) T {
......@@ -364,6 +371,28 @@ fn rintf(x: f32) callconv(.c) f32 {
364371 return y;
365372}
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
367396fn tanh(x: f64) callconv(.c) f64 {
368397 return math.tanh(x);
369398}
lib/compiler_rt/cos.zig+1-1
......@@ -7,6 +7,7 @@
77
88const std = @import("std");
99const math = std.math;
10const ld = math.long_double;
1011const mem = std.mem;
1112const expect = std.testing.expect;
1213const expectApproxEqAbs = std.testing.expectApproxEqAbs;
......@@ -17,7 +18,6 @@ const trig = @import("trig.zig");
1718const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
1819const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
1920const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l;
20const ld = @import("long_double.zig");
2121
2222comptime {
2323 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 @@
55
66const std = @import("std");
77const math = std.math;
8const ld = math.long_double;
89
9const ld = @import("long_double.zig");
1010const rem_pio2_large = @import("rem_pio2_large.zig").rem_pio2_large;
1111
1212pub fn rem_pio2l(comptime T: type, x: T, y: *[2]T) i32 {
lib/compiler_rt/sin.zig+1-1
......@@ -7,6 +7,7 @@
77
88const std = @import("std");
99const math = std.math;
10const ld = math.long_double;
1011const mem = std.mem;
1112const expect = std.testing.expect;
1213const expectApproxEqAbs = std.testing.expectApproxEqAbs;
......@@ -17,7 +18,6 @@ const trig = @import("trig.zig");
1718const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
1819const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
1920const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l;
20const ld = @import("long_double.zig");
2121
2222comptime {
2323 symbol(&sinh, "__sinh");
lib/compiler_rt/sincos.zig+1-1
......@@ -2,6 +2,7 @@ const std = @import("std");
22const builtin = @import("builtin");
33const arch = builtin.cpu.arch;
44const math = std.math;
5const ld = math.long_double;
56const mem = std.mem;
67const expect = std.testing.expect;
78const expectApproxEqAbs = std.testing.expectApproxEqAbs;
......@@ -9,7 +10,6 @@ const trig = @import("trig.zig");
910const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
1011const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
1112const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l;
12const ld = @import("long_double.zig");
1313const compiler_rt = @import("../compiler_rt.zig");
1414const symbol = compiler_rt.symbol;
1515
lib/compiler_rt/tan.zig+1-1
......@@ -9,6 +9,7 @@
99const std = @import("std");
1010const builtin = @import("builtin");
1111const math = std.math;
12const ld = math.long_double;
1213const mem = std.mem;
1314const expect = std.testing.expect;
1415const expectApproxEqAbs = std.testing.expectApproxEqAbs;
......@@ -17,7 +18,6 @@ const kernel = @import("trig.zig");
1718const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
1819const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
1920const rem_pio2l = @import("rem_pio2l.zig").rem_pio2l;
20const ld = @import("long_double.zig");
2121
2222const arch = builtin.cpu.arch;
2323const 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;
5858pub const floatEps = float.floatEps;
5959pub const floatEpsAt = float.floatEpsAt;
6060pub const inf = float.inf;
61pub const long_double = float.long_double;
6162pub const nan = float.nan;
6263pub const snan = float.snan;
6364
lib/std/math/float.zig+62
......@@ -4,6 +4,68 @@ const assert = std.debug.assert;
44const expect = std.testing.expect;
55const 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
769pub fn FloatRepr(comptime Float: type) type {
870 const fractional_bits = floatFractionalBits(Float);
971 const exponent_bits = floatExponentBits(Float);
src/libs/mingw.zig-2
......@@ -853,9 +853,7 @@ const mingw32_x86_src = [_][]const u8{
853853 "math" ++ path.sep_str ++ "fmal.c",
854854 "math" ++ path.sep_str ++ "llrintl.c",
855855 "math" ++ path.sep_str ++ "llroundl.c",
856 "math" ++ path.sep_str ++ "lrintl.c",
857856 "math" ++ path.sep_str ++ "lroundl.c",
858 "math" ++ path.sep_str ++ "rintl.c",
859857 "math" ++ path.sep_str ++ "tgammal.c",
860858 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "_chgsignl.S",
861859 "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acoshl.c",
src/libs/musl.zig-9
......@@ -850,14 +850,12 @@ const src_files = [_][]const u8{
850850 "musl/src/math/i386/log1p.s",
851851 "musl/src/math/i386/log2l.s",
852852 "musl/src/math/i386/logl.s",
853 "musl/src/math/i386/lrintl.c",
854853 "musl/src/math/i386/remainder.c",
855854 "musl/src/math/i386/remainderf.c",
856855 "musl/src/math/i386/remainderl.c",
857856 "musl/src/math/i386/remquof.s",
858857 "musl/src/math/i386/remquol.s",
859858 "musl/src/math/i386/remquo.s",
860 "musl/src/math/i386/rintl.c",
861859 "musl/src/math/i386/scalblnf.s",
862860 "musl/src/math/i386/scalblnl.s",
863861 "musl/src/math/i386/scalbln.s",
......@@ -897,7 +895,6 @@ const src_files = [_][]const u8{
897895 "musl/src/math/logbf.c",
898896 "musl/src/math/logbl.c",
899897 "musl/src/math/logl.c",
900 "musl/src/math/lrintl.c",
901898 "musl/src/math/lround.c",
902899 "musl/src/math/lroundf.c",
903900 "musl/src/math/lroundl.c",
......@@ -943,7 +940,6 @@ const src_files = [_][]const u8{
943940 "musl/src/math/remquo.c",
944941 "musl/src/math/remquof.c",
945942 "musl/src/math/remquol.c",
946 "musl/src/math/rintl.c",
947943 "musl/src/math/riscv32/fma.c",
948944 "musl/src/math/riscv32/fmaf.c",
949945 "musl/src/math/riscv64/fma.c",
......@@ -953,7 +949,6 @@ const src_files = [_][]const u8{
953949 "musl/src/math/s390x/nearbyint.c",
954950 "musl/src/math/s390x/nearbyintf.c",
955951 "musl/src/math/s390x/nearbyintl.c",
956 "musl/src/math/s390x/rintl.c",
957952 "musl/src/math/scalb.c",
958953 "musl/src/math/scalbf.c",
959954 "musl/src/math/scalbln.c",
......@@ -995,9 +990,7 @@ const src_files = [_][]const u8{
995990 "musl/src/math/x32/log1pl.s",
996991 "musl/src/math/x32/log2l.s",
997992 "musl/src/math/x32/logl.s",
998 "musl/src/math/x32/lrintl.s",
999993 "musl/src/math/x32/remainderl.s",
1000 "musl/src/math/x32/rintl.s",
1001994 "musl/src/math/x86_64/acosl.s",
1002995 "musl/src/math/x86_64/asinl.s",
1003996 "musl/src/math/x86_64/atan2l.s",
......@@ -1014,10 +1007,8 @@ const src_files = [_][]const u8{
10141007 "musl/src/math/x86_64/log1pl.s",
10151008 "musl/src/math/x86_64/log2l.s",
10161009 "musl/src/math/x86_64/logl.s",
1017 "musl/src/math/x86_64/lrintl.c",
10181010 "musl/src/math/x86_64/remainderl.c",
10191011 "musl/src/math/x86_64/remquol.c",
1020 "musl/src/math/x86_64/rintl.c",
10211012 "musl/src/misc/a64l.c",
10221013 "musl/src/misc/basename.c",
10231014 "musl/src/misc/dirname.c",
src/libs/wasi_libc.zig-2
......@@ -727,7 +727,6 @@ const libc_top_half_src_files = [_][]const u8{
727727 "musl/src/math/logbf.c",
728728 "musl/src/math/logbl.c",
729729 "musl/src/math/logl.c",
730 "musl/src/math/lrintl.c",
731730 "musl/src/math/lround.c",
732731 "musl/src/math/lroundf.c",
733732 "musl/src/math/lroundl.c",
......@@ -761,7 +760,6 @@ const libc_top_half_src_files = [_][]const u8{
761760 "musl/src/math/remquo.c",
762761 "musl/src/math/remquof.c",
763762 "musl/src/math/remquol.c",
764 "musl/src/math/rintl.c",
765763 "musl/src/math/scalb.c",
766764 "musl/src/math/scalbf.c",
767765 "musl/src/math/scalbln.c",
test/c/math.zig+95-35
......@@ -3,14 +3,19 @@ const std = @import("std");
33
44const c = std.c;
55const math = std.math;
6
67const testing = std.testing;
8const expect = testing.expect;
9const expectEqual = testing.expectEqual;
10const expectApproxEqAbs = testing.expectApproxEqAbs;
11const expectApproxEqRel = testing.expectApproxEqRel;
712
813fn testModf(comptime T: type) !void {
914 const f = switch (T) {
1015 f32 => c.modff,
1116 f64 => c.modf,
1217 c_longdouble => c.modfl,
13 else => unreachable,
18 else => @compileError("modf not implemented for " ++ @typeName(T)),
1419 };
1520
1621 var int: T = undefined;
......@@ -20,85 +25,140 @@ fn testModf(comptime T: type) !void {
2025 const normal_frac = f(@as(T, 1234.567), iptr);
2126 // Account for precision error
2227 const expected = 1234.567 - @as(T, 1234);
23 try testing.expectApproxEqAbs(expected, normal_frac, eps_val);
24 try testing.expectApproxEqRel(@as(T, 1234.0), iptr.*, eps_val);
28 try expectApproxEqAbs(expected, normal_frac, eps_val);
29 try expectApproxEqRel(@as(T, 1234.0), iptr.*, eps_val);
2530
2631 // When `x` is a NaN, NaN is returned and `*iptr` is set to NaN
2732 const nan_frac = f(math.nan(T), iptr);
28 try testing.expect(math.isNan(nan_frac));
29 try testing.expect(math.isNan(iptr.*));
33 try expect(math.isNan(nan_frac));
34 try expect(math.isNan(iptr.*));
3035
3136 // When `x` is positive infinity, +0 is returned and `*iptr` is set to
3237 // positive infinity
3338 const pos_zero_frac = f(math.inf(T), iptr);
34 try testing.expect(math.isPositiveZero(pos_zero_frac));
35 try testing.expect(math.isPositiveInf(iptr.*));
39 try expectEqual(0.0, pos_zero_frac);
40 try expect(math.isPositiveInf(iptr.*));
3641
3742 // When `x` is negative infinity, -0 is returned and `*iptr` is set to
3843 // negative infinity
3944 const neg_zero_frac = f(-math.inf(T), iptr);
40 try testing.expect(math.isNegativeZero(neg_zero_frac));
41 try testing.expect(math.isNegativeInf(iptr.*));
45 try expectEqual(-0.0, neg_zero_frac);
46 try expect(math.isNegativeInf(iptr.*));
4247
4348 // Return -0 when `x` is a negative integer
4449 const nz_frac = f(@as(T, -1000.0), iptr);
45 try testing.expect(math.isNegativeZero(nz_frac));
46 try testing.expectEqual(@as(T, -1000.0), iptr.*);
50 try expectEqual(-0.0, nz_frac);
51 try expectEqual(@as(T, -1000.0), iptr.*);
4752
4853 // Return +0 when `x` is a positive integer
4954 const pz_frac = f(@as(T, 1000.0), iptr);
50 try testing.expect(math.isPositiveZero(pz_frac));
51 try testing.expectEqual(@as(T, 1000.0), iptr.*);
55 try expectEqual(0.0, pz_frac);
56 try expectEqual(@as(T, 1000.0), iptr.*);
5257}
5358
5459test "modf" {
55 try testModf(f32);
5660 try testModf(f64);
61}
5762
58 if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO
63test "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
6070 try testModf(c_longdouble);
6171}
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 {
6490 const f = switch (T) {
6591 f32 => c.rintf,
6692 f64 => c.rint,
93 c_longdouble => c.rintl,
6794 else => @compileError("rint not implemented for" ++ @typeName(T)),
6895 };
6996
7097 // Positive numbers round correctly
71 try testing.expectEqual(@as(T, 42.0), f(42.2));
72 try testing.expectEqual(@as(T, 42.0), f(41.8));
98 try expectEqual(@as(T, 42.0), f(42.2));
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
74102 // Negative numbers round correctly
75 try testing.expectEqual(@as(T, -6.0), f(-5.9));
76 try testing.expectEqual(@as(T, -6.0), f(-6.1));
103 try expectEqual(@as(T, -6.0), f(-5.9));
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
78112 // No rounding needed test
79 try testing.expectEqual(@as(T, 5.0), f(5.0));
80 try testing.expectEqual(@as(T, -10.0), f(-10.0));
81 try testing.expectEqual(@as(T, 0.0), f(0.0));
113 try expectEqual(@as(T, 5.0), f(5.0));
114 try expectEqual(@as(T, -10.0), f(-10.0));
115 try expectEqual(@as(T, 0.0), f(0.0));
82116
83117 // Very large numbers return unchanged
84118 const large: T = 9007199254740992.0; // 2^53
85 try testing.expectEqual(large, f(large));
86 try testing.expectEqual(-large, f(-large));
119 try expectEqual(large, f(large));
120 try expectEqual(-large, f(-large));
87121
88122 // Small positive numbers round to zero
89 const pos_result = f(0.3);
90 try testing.expect(math.isPositiveZero(pos_result));
123 try expectEqual(@as(T, 0.0), f(0.3));
91124
92 // Small negative numbers round to negative zero
93 const neg_result = f(-0.3);
94 try testing.expect(math.isNegativeZero(neg_result));
125 // TODO: negative `long double`s close to `-n.5` seem to round to `-n.5`
126 // instead of either `-n.0` or `-(n-1).0` on NetBSD. For example, this
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
96133 // Exact half rounds to nearest even (banker's rounding)
97 try testing.expectEqual(@as(T, 2.0), f(2.5));
98 try testing.expectEqual(@as(T, 4.0), f(3.5));
134 try expectEqual(@as(T, 2.0), f(2.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);
99148}
100149
101test "rint" {
102 try testRint(f32);
103 try testRint(f64);
150test "rint.normal" {
151 try testRintNormal(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);
104164}
test/libc.zig+1-1
......@@ -295,7 +295,7 @@ pub fn addCases(cases: *tests.LibcContext) void {
295295 cases.addLibcTestCase("math/remquol.c", true, .{});
296296 cases.addLibcTestCase("math/rint.c", true, .{});
297297 cases.addLibcTestCase("math/rintf.c", true, .{});
298 // cases.addLibcTestCase("math/rintl.c", true, .{});
298 cases.addLibcTestCase("math/rintl.c", true, .{});
299299 cases.addLibcTestCase("math/round.c", true, .{});
300300 cases.addLibcTestCase("math/roundf.c", true, .{});
301301 cases.addLibcTestCase("math/roundl.c", true, .{});