mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 15:42:49 +00:00
a2df84d0ff
- Changed `modf_result` to `Modf` to better fit naming conventions - Reworked `modf` to be far simpler and support all floating point types (as well as vectors) (I have done benchmarks and can confirm that the performance is roughly equivalent to the old implementation) - Added more descriptive tests for modf - Deprecated `modf32_result` and `modf64_result` in favor of `Modf(f32)` and `Modf(f64)` respectively
142 lines
4.5 KiB
Zig
142 lines
4.5 KiB
Zig
const std = @import("../std.zig");
|
|
const math = std.math;
|
|
const expect = std.testing.expect;
|
|
const expectEqual = std.testing.expectEqual;
|
|
const expectApproxEqAbs = std.testing.expectApproxEqAbs;
|
|
|
|
pub fn Modf(comptime T: type) type {
|
|
return struct {
|
|
fpart: T,
|
|
ipart: T,
|
|
};
|
|
}
|
|
|
|
/// Returns the integer and fractional floating-point numbers that sum to x. The sign of each
|
|
/// result is the same as the sign of x.
|
|
/// In comptime, may be used with comptime_float
|
|
///
|
|
/// Special Cases:
|
|
/// - modf(+-inf) = +-inf, nan
|
|
/// - modf(nan) = nan, nan
|
|
pub fn modf(x: anytype) Modf(@TypeOf(x)) {
|
|
const ipart = @trunc(x);
|
|
return .{
|
|
.ipart = ipart,
|
|
.fpart = x - ipart,
|
|
};
|
|
}
|
|
|
|
test modf {
|
|
inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
|
|
const epsilon: comptime_float = @max(1e-6, math.floatEps(T));
|
|
|
|
var r: Modf(T) = undefined;
|
|
|
|
r = modf(@as(T, 1.0));
|
|
try expectEqual(1.0, r.ipart);
|
|
try expectEqual(0.0, r.fpart);
|
|
|
|
r = modf(@as(T, 0.34682));
|
|
try expectEqual(0.0, r.ipart);
|
|
try expectApproxEqAbs(@as(T, 0.34682), r.fpart, epsilon);
|
|
|
|
r = modf(@as(T, 2.54576));
|
|
try expectEqual(2.0, r.ipart);
|
|
try expectApproxEqAbs(0.54576, r.fpart, epsilon);
|
|
|
|
r = modf(@as(T, 3.9782));
|
|
try expectEqual(3.0, r.ipart);
|
|
try expectApproxEqAbs(0.9782, r.fpart, epsilon);
|
|
}
|
|
}
|
|
|
|
/// Generate a namespace of tests for modf on values of the given type
|
|
fn ModfTests(comptime T: type) type {
|
|
return struct {
|
|
test "normal" {
|
|
const epsilon: comptime_float = @max(1e-6, math.floatEps(T));
|
|
var r: Modf(T) = undefined;
|
|
|
|
r = modf(@as(T, 1.0));
|
|
try expectEqual(1.0, r.ipart);
|
|
try expectEqual(0.0, r.fpart);
|
|
|
|
r = modf(@as(T, 0.34682));
|
|
try expectEqual(0.0, r.ipart);
|
|
try expectApproxEqAbs(0.34682, r.fpart, epsilon);
|
|
|
|
r = modf(@as(T, 3.97812));
|
|
try expectEqual(3.0, r.ipart);
|
|
// account for precision error
|
|
const expected_a: T = 3.97812 - @as(T, 3);
|
|
try expectApproxEqAbs(expected_a, r.fpart, epsilon);
|
|
|
|
r = modf(@as(T, 43874.3));
|
|
try expectEqual(43874.0, r.ipart);
|
|
// account for precision error
|
|
const expected_b: T = 43874.3 - @as(T, 43874);
|
|
try expectApproxEqAbs(expected_b, r.fpart, epsilon);
|
|
|
|
r = modf(@as(T, 1234.340780));
|
|
try expectEqual(1234.0, r.ipart);
|
|
// account for precision error
|
|
const expected_c: T = 1234.340780 - @as(T, 1234);
|
|
try expectApproxEqAbs(expected_c, r.fpart, epsilon);
|
|
}
|
|
test "vector" {
|
|
// Currently, a compiler bug is breaking the usage
|
|
// of @trunc on @Vector types
|
|
|
|
// TODO: Repopulate the below array and
|
|
// remove the skip statement once this
|
|
// bug is fixed
|
|
|
|
// const widths = [_]comptime_int{ 1, 2, 3, 4, 8, 16 };
|
|
const widths = [_]comptime_int{};
|
|
|
|
if (widths.len == 0)
|
|
return error.SkipZigTest;
|
|
|
|
inline for (widths) |len| {
|
|
const V: type = @Vector(len, T);
|
|
var r: Modf(V) = undefined;
|
|
|
|
r = modf(@as(V, @splat(1.0)));
|
|
try expectEqual(@as(V, @splat(1.0)), r.ipart);
|
|
try expectEqual(@as(V, @splat(0.0)), r.fpart);
|
|
|
|
r = modf(@as(V, @splat(2.75)));
|
|
try expectEqual(@as(V, @splat(2.0)), r.ipart);
|
|
try expectEqual(@as(V, @splat(0.75)), r.fpart);
|
|
|
|
r = modf(@as(V, @splat(0.2)));
|
|
try expectEqual(@as(V, @splat(0.0)), r.ipart);
|
|
try expectEqual(@as(V, @splat(0.2)), r.fpart);
|
|
|
|
r = modf(std.simd.iota(T, len) + @as(V, @splat(0.5)));
|
|
try expectEqual(std.simd.iota(T, len), r.ipart);
|
|
try expectEqual(@as(V, @splat(0.5)), r.fpart);
|
|
}
|
|
}
|
|
test "inf" {
|
|
var r: Modf(T) = undefined;
|
|
|
|
r = modf(math.inf(T));
|
|
try expect(math.isPositiveInf(r.ipart) and math.isNan(r.fpart));
|
|
|
|
r = modf(-math.inf(T));
|
|
try expect(math.isNegativeInf(r.ipart) and math.isNan(r.fpart));
|
|
}
|
|
test "nan" {
|
|
const r: Modf(T) = modf(math.nan(T));
|
|
try expect(math.isNan(r.ipart) and math.isNan(r.fpart));
|
|
}
|
|
};
|
|
}
|
|
|
|
comptime {
|
|
for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
|
|
_ = ModfTests(T);
|
|
}
|
|
}
|