mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-28 03:17:08 +03:00
764760df62
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>
165 lines
5.0 KiB
Zig
165 lines
5.0 KiB
Zig
const builtin = @import("builtin");
|
|
const std = @import("std");
|
|
|
|
const c = std.c;
|
|
const math = std.math;
|
|
|
|
const testing = std.testing;
|
|
const expect = testing.expect;
|
|
const expectEqual = testing.expectEqual;
|
|
const expectApproxEqAbs = testing.expectApproxEqAbs;
|
|
const expectApproxEqRel = testing.expectApproxEqRel;
|
|
|
|
fn testModf(comptime T: type) !void {
|
|
const f = switch (T) {
|
|
f32 => c.modff,
|
|
f64 => c.modf,
|
|
c_longdouble => c.modfl,
|
|
else => @compileError("modf not implemented for " ++ @typeName(T)),
|
|
};
|
|
|
|
var int: T = undefined;
|
|
const iptr = ∫
|
|
const eps_val: comptime_float = @max(1e-6, math.floatEps(T));
|
|
|
|
const normal_frac = f(@as(T, 1234.567), iptr);
|
|
// Account for precision error
|
|
const expected = 1234.567 - @as(T, 1234);
|
|
try expectApproxEqAbs(expected, normal_frac, eps_val);
|
|
try expectApproxEqRel(@as(T, 1234.0), iptr.*, eps_val);
|
|
|
|
// When `x` is a NaN, NaN is returned and `*iptr` is set to NaN
|
|
const nan_frac = f(math.nan(T), iptr);
|
|
try expect(math.isNan(nan_frac));
|
|
try expect(math.isNan(iptr.*));
|
|
|
|
// When `x` is positive infinity, +0 is returned and `*iptr` is set to
|
|
// positive infinity
|
|
const pos_zero_frac = f(math.inf(T), iptr);
|
|
try expectEqual(0.0, pos_zero_frac);
|
|
try expect(math.isPositiveInf(iptr.*));
|
|
|
|
// When `x` is negative infinity, -0 is returned and `*iptr` is set to
|
|
// negative infinity
|
|
const neg_zero_frac = f(-math.inf(T), iptr);
|
|
try expectEqual(-0.0, neg_zero_frac);
|
|
try expect(math.isNegativeInf(iptr.*));
|
|
|
|
// Return -0 when `x` is a negative integer
|
|
const nz_frac = f(@as(T, -1000.0), iptr);
|
|
try expectEqual(-0.0, nz_frac);
|
|
try expectEqual(@as(T, -1000.0), iptr.*);
|
|
|
|
// Return +0 when `x` is a positive integer
|
|
const pz_frac = f(@as(T, 1000.0), iptr);
|
|
try expectEqual(0.0, pz_frac);
|
|
try expectEqual(@as(T, 1000.0), iptr.*);
|
|
}
|
|
|
|
test "modf" {
|
|
try testModf(f64);
|
|
}
|
|
|
|
test "modff" {
|
|
try testModf(f32);
|
|
}
|
|
|
|
test "modfl" {
|
|
if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO: see https://codeberg.org/ziglang/zig/issues/30976
|
|
|
|
try testModf(c_longdouble);
|
|
}
|
|
|
|
fn testRintSpecial(comptime T: type) !void {
|
|
const f = switch (T) {
|
|
f32 => c.rintf,
|
|
f64 => c.rint,
|
|
c_longdouble => c.rintl,
|
|
else => @compileError("rint not implemented for" ++ @typeName(T)),
|
|
};
|
|
|
|
// For the special cases, x itself should be returned
|
|
try expectEqual(0.0, f(0.0));
|
|
try expectEqual(-0.0, f(-0.0));
|
|
try expectEqual(math.inf(T), f(math.inf(T)));
|
|
try expectEqual(-math.inf(T), f(-math.inf(T)));
|
|
try expect(math.isNan(f(math.nan(T))));
|
|
}
|
|
|
|
fn testRintNormal(comptime T: type) !void {
|
|
const f = switch (T) {
|
|
f32 => c.rintf,
|
|
f64 => c.rint,
|
|
c_longdouble => c.rintl,
|
|
else => @compileError("rint not implemented for" ++ @typeName(T)),
|
|
};
|
|
|
|
// Positive numbers round correctly
|
|
try expectEqual(@as(T, 42.0), f(42.2));
|
|
try expectEqual(@as(T, 42.0), f(41.8));
|
|
try expectEqual(@as(T, 16_777_216.0), f(16_777_215.6));
|
|
|
|
// Negative numbers round correctly
|
|
try expectEqual(@as(T, -6.0), f(-5.9));
|
|
try expectEqual(@as(T, -6.0), f(-6.1));
|
|
// TODO: negative `long double`s close to `-n.5` seem to round to `-n.5`
|
|
// instead of either `-n.0` or `-(n-1).0` on NetBSD. For example, this
|
|
// case would round to `-16_777_215.5`.
|
|
if (!(T == c_longdouble and builtin.target.os.tag == .netbsd)) {
|
|
try expectEqual(@as(T, -16_777_215.0), f(-16_777_215.4));
|
|
}
|
|
|
|
// No rounding needed test
|
|
try expectEqual(@as(T, 5.0), f(5.0));
|
|
try expectEqual(@as(T, -10.0), f(-10.0));
|
|
try expectEqual(@as(T, 0.0), f(0.0));
|
|
|
|
// Very large numbers return unchanged
|
|
const large: T = 9007199254740992.0; // 2^53
|
|
try expectEqual(large, f(large));
|
|
try expectEqual(-large, f(-large));
|
|
|
|
// Small positive numbers round to zero
|
|
try expectEqual(@as(T, 0.0), f(0.3));
|
|
|
|
// TODO: negative `long double`s close to `-n.5` seem to round to `-n.5`
|
|
// instead of either `-n.0` or `-(n-1).0` on NetBSD. For example, this
|
|
// case would round to `-0.5`.
|
|
if (!(T == c_longdouble and builtin.target.os.tag == .netbsd)) {
|
|
// Small negative numbers round to negative zero
|
|
try expectEqual(@as(T, -0.0), f(-0.3));
|
|
}
|
|
|
|
// Exact half rounds to nearest even (banker's rounding)
|
|
try expectEqual(@as(T, 2.0), f(2.5));
|
|
try expectEqual(@as(T, 4.0), f(3.5));
|
|
}
|
|
|
|
test "rintf.special" {
|
|
try testRintSpecial(f32);
|
|
}
|
|
|
|
test "rintf.normal" {
|
|
try testRintNormal(f32);
|
|
}
|
|
|
|
test "rint.special" {
|
|
try testRintSpecial(f64);
|
|
}
|
|
|
|
test "rint.normal" {
|
|
try testRintNormal(f64);
|
|
}
|
|
|
|
test "rintl.special" {
|
|
if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO: see https://codeberg.org/ziglang/zig/issues/30976
|
|
|
|
try testRintSpecial(c_longdouble);
|
|
}
|
|
|
|
test "rintl.normal" {
|
|
if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO: see https://codeberg.org/ziglang/zig/issues/30976
|
|
|
|
try testRintNormal(c_longdouble);
|
|
}
|