authorgravatar for hi@mihaelm.commihael <hi@mihaelm.com> 2026-03-20 20:14:25+01:00
committergravatar for hi@mihaelm.commihael <hi@mihaelm.com> 2026-04-02 23:54:19+02:00
log245c160a7b97e1dc8e0fd04e706d7abb36ac9f0c
treed6f3b1fbcc1d43a3cd0a53933541a68d041e2b51
parent811bada06d4d88d786119bab752c0e0cff345154
signaturebadge-check Signed by SSH key SHA256:aoFoShdYLdrqMichqKXSSieTKUfACUIDJHsKc4V2tQg

`libzigc/math`: Implement `rintf`

The implementation was ported from `musl` to Zig code, and the `rint` unit tests were generalized so they could be used for `rintf` as well. This was checked both through unit tests and running `libc-test` suite: ``` $ ./build/stage3/bin/zig build -p stage4 -Denable-llvm -Dno-lib $ stage4/bin/zig build test-libc -Dlibc-test-path=<LIBC-TEST-PATH> -Dtest-filter=rintf -fqemu -fwasmtime --summary line Build Summary: 1657/1657 steps succeeded ```

8 files changed, 56 insertions(+), 97 deletions(-)

lib/c/math.zig+56-21
......@@ -48,6 +48,10 @@ comptime {
4848 symbol(&tanhf, "tanhf");
4949 }
5050
51 if (builtin.target.isMinGW() or builtin.target.isMuslLibC()) {
52 symbol(&rintf, "rintf");
53 }
54
5155 if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) {
5256 symbol(&acos, "acos");
5357 symbol(&acosf, "acosf");
......@@ -348,7 +352,7 @@ fn pow10f(x: f32) callconv(.c) f32 {
348352}
349353
350354fn rint(x: f64) callconv(.c) f64 {
351 const toint: f64 = 1.0 / @as(f64, math.floatEps(f64));
355 const toint: f64 = 1.0 / math.floatEps(f64);
352356 const a: u64 = @bitCast(x);
353357 const e = a >> 52 & 0x7ff;
354358 const s = a >> 63;
......@@ -368,39 +372,70 @@ fn rint(x: f64) callconv(.c) f64 {
368372 return y;
369373}
370374
371test "rint" {
375fn rintf(x: f32) callconv(.c) f32 {
376 const toint: f32 = 1.0 / math.floatEps(f32);
377 const a: u32 = @bitCast(x);
378 const e = a >> 23 & 0xff;
379 const s = a >> 31;
380 var y: f32 = undefined;
381
382 if (e >= 0x7f + 23) {
383 return x;
384 }
385
386 if (s == 1) {
387 y = x - toint + toint;
388 } else {
389 y = x + toint - toint;
390 }
391
392 if (y == 0) {
393 return if (s == 1) -0.0 else 0;
394 }
395 return y;
396}
397
398fn testRint(comptime T: type) !void {
399 const f = switch (T) {
400 f32 => rintf,
401 f64 => rint,
402 else => @compileError("rint not implemented for" ++ @typeName(T)),
403 };
404
372405 // Positive numbers round correctly
373 try expectEqual(@as(f64, 42.0), rint(42.2));
374 try expectEqual(@as(f64, 42.0), rint(41.8));
406 try expectEqual(@as(T, 42.0), f(42.2));
407 try expectEqual(@as(T, 42.0), f(41.8));
375408
376409 // Negative numbers round correctly
377 try expectEqual(@as(f64, -6.0), rint(-5.9));
378 try expectEqual(@as(f64, -6.0), rint(-6.1));
410 try expectEqual(@as(T, -6.0), f(-5.9));
411 try expectEqual(@as(T, -6.0), f(-6.1));
379412
380413 // No rounding needed test
381 try expectEqual(@as(f64, 5.0), rint(5.0));
382 try expectEqual(@as(f64, -10.0), rint(-10.0));
383 try expectEqual(@as(f64, 0.0), rint(0.0));
414 try expectEqual(@as(T, 5.0), f(5.0));
415 try expectEqual(@as(T, -10.0), f(-10.0));
416 try expectEqual(@as(T, 0.0), f(0.0));
384417
385418 // Very large numbers return unchanged
386 const large: f64 = 9007199254740992.0; // 2^53
387 try expectEqual(large, rint(large));
388 try expectEqual(-large, rint(-large));
419 const large: T = 9007199254740992.0; // 2^53
420 try expectEqual(large, f(large));
421 try expectEqual(-large, f(-large));
389422
390423 // Small positive numbers round to zero
391 const pos_result = rint(0.3);
392 try expectEqual(@as(f64, 0.0), pos_result);
393 try expect(@as(u64, @bitCast(pos_result)) == 0);
424 const pos_result = f(0.3);
425 try expect(math.isPositiveZero(pos_result));
394426
395427 // Small negative numbers round to negative zero
396 const neg_result = rint(-0.3);
397 try expectEqual(@as(f64, 0.0), neg_result);
398 const bits: u64 = @bitCast(neg_result);
399 try expect((bits >> 63) == 1);
428 const neg_result = f(-0.3);
429 try expect(math.isNegativeZero(neg_result));
400430
401431 // Exact half rounds to nearest even (banker's rounding)
402 try expectEqual(@as(f64, 2.0), rint(2.5));
403 try expectEqual(@as(f64, 4.0), rint(3.5));
432 try expectEqual(@as(T, 2.0), f(2.5));
433 try expectEqual(@as(T, 4.0), f(3.5));
434}
435
436test "rint" {
437 try testRint(f32);
438 try testRint(f64);
404439}
405440
406441fn tanh(x: f64) callconv(.c) f64 {
lib/libc/mingw/math/arm64/rintf.c deleted-12
......@@ -1,12 +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
8float rintf (float x) {
9 float retval = 0.0F;
10 __asm__ __volatile__ ("frintx %s0, %s1\n\t" : "=w" (retval) : "w" (x));
11 return retval;
12}
lib/libc/musl/src/math/aarch64/rintf.c deleted-7
......@@ -1,7 +0,0 @@
1#include <math.h>
2
3float rintf(float x)
4{
5 __asm__ ("frintx %s0, %s1" : "=w"(x) : "w"(x));
6 return x;
7}
lib/libc/musl/src/math/i386/rintf.c deleted-7
......@@ -1,7 +0,0 @@
1#include <math.h>
2
3float rintf(float x)
4{
5 __asm__ ("frndint" : "+t"(x));
6 return x;
7}
lib/libc/musl/src/math/rintf.c deleted-30
......@@ -1,30 +0,0 @@
1#include <float.h>
2#include <math.h>
3#include <stdint.h>
4
5#if FLT_EVAL_METHOD==0
6#define EPS FLT_EPSILON
7#elif FLT_EVAL_METHOD==1
8#define EPS DBL_EPSILON
9#elif FLT_EVAL_METHOD==2
10#define EPS LDBL_EPSILON
11#endif
12static const float_t toint = 1/EPS;
13
14float rintf(float x)
15{
16 union {float f; uint32_t i;} u = {x};
17 int e = u.i>>23 & 0xff;
18 int s = u.i>>31;
19 float_t y;
20
21 if (e >= 0x7f+23)
22 return x;
23 if (s)
24 y = x - toint + toint;
25 else
26 y = x + toint - toint;
27 if (y == 0)
28 return s ? -0.0f : 0.0f;
29 return y;
30}
lib/libc/musl/src/math/s390x/rintf.c deleted-15
......@@ -1,15 +0,0 @@
1#include <math.h>
2
3#if defined(__HTM__) || __ARCH__ >= 9
4
5float rintf(float x)
6{
7 __asm__ ("fiebr %0, 0, %1" : "=f"(x) : "f"(x));
8 return x;
9}
10
11#else
12
13#include "../rintf.c"
14
15#endif
src/libs/mingw.zig-1
......@@ -996,7 +996,6 @@ const mingw32_arm32_src = [_][]const u8{
996996const mingw32_arm64_src = [_][]const u8{
997997 // mingwex
998998 "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "rint.c",
999 "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "rintf.c",
1000999 "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "sincos.S",
10011000};
10021001
src/libs/musl.zig-4
......@@ -792,7 +792,6 @@ const src_files = [_][]const u8{
792792 "musl/src/math/aarch64/lroundf.c",
793793 "musl/src/math/aarch64/nearbyint.c",
794794 "musl/src/math/aarch64/nearbyintf.c",
795 "musl/src/math/aarch64/rintf.c",
796795 "musl/src/math/acosh.c",
797796 "musl/src/math/acoshl.c",
798797 "musl/src/math/acosl.c",
......@@ -867,7 +866,6 @@ const src_files = [_][]const u8{
867866 "musl/src/math/i386/remquof.s",
868867 "musl/src/math/i386/remquol.s",
869868 "musl/src/math/i386/remquo.s",
870 "musl/src/math/i386/rintf.c",
871869 "musl/src/math/i386/rintl.c",
872870 "musl/src/math/i386/scalblnf.s",
873871 "musl/src/math/i386/scalblnl.s",
......@@ -955,7 +953,6 @@ const src_files = [_][]const u8{
955953 "musl/src/math/remquo.c",
956954 "musl/src/math/remquof.c",
957955 "musl/src/math/remquol.c",
958 "musl/src/math/rintf.c",
959956 "musl/src/math/rintl.c",
960957 "musl/src/math/riscv32/fma.c",
961958 "musl/src/math/riscv32/fmaf.c",
......@@ -966,7 +963,6 @@ const src_files = [_][]const u8{
966963 "musl/src/math/s390x/nearbyint.c",
967964 "musl/src/math/s390x/nearbyintf.c",
968965 "musl/src/math/s390x/nearbyintl.c",
969 "musl/src/math/s390x/rintf.c",
970966 "musl/src/math/s390x/rintl.c",
971967 "musl/src/math/scalb.c",
972968 "musl/src/math/scalbf.c",