diff options
| author | LemonBoy <thatlemon@gmail.com> | 2021-03-13 13:08:46 +0100 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-03-14 17:23:47 -0400 |
| commit | 27d07c6c4de36af1186392d4bec321825403860e (patch) | |
| tree | 39a45001ab3c67640e112fed1a61342ff54101f7 | |
| parent | d33f0d3375c621db479fbf565855c7d2a7eea735 (diff) | |
| download | zig-27d07c6c4de36af1186392d4bec321825403860e.tar.gz zig-27d07c6c4de36af1186392d4bec321825403860e.zip | |
std: Replace testing fns for floating-point values
Beside handling NaNs and other non-numeric values better we finally
offer the same pair of testing predicates in math and testing.
| -rw-r--r-- | lib/std/os/linux/io_uring.zig | 2 | ||||
| -rw-r--r-- | lib/std/testing.zig | 74 | ||||
| -rw-r--r-- | test/stage1/behavior/vector.zig | 12 |
3 files changed, 47 insertions, 41 deletions
diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index e900bdcd6a..4342beca00 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -1353,7 +1353,7 @@ test "timeout (after a relative time)" { .res = -linux.ETIME, .flags = 0, }, cqe); - testing.expectWithinMargin(@intToFloat(f64, ms), @intToFloat(f64, stopped - started), margin); + testing.expectApproxEqAbs(@intToFloat(f64, ms), @intToFloat(f64, stopped - started), margin); } test "timeout (after a number of completions)" { diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 1d89155a58..67831c92fb 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -200,67 +200,69 @@ pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anyt return error.TestFailed; } -/// This function is intended to be used only in tests. When the actual value is not -/// within the margin of the expected value, -/// prints diagnostics to stderr to show exactly how they are not equal, then aborts. +pub const expectWithinMargin = @compileError("expectWithinMargin is deprecated, use expectApproxEqAbs or expectApproxEqRel"); +pub const expectWithinEpsilon = @compileError("expectWithinEpsilon is deprecated, use expectApproxEqAbs or expectApproxEqRel"); + +/// This function is intended to be used only in tests. When the actual value is +/// not approximately equal to the expected value, prints diagnostics to stderr +/// to show exactly how they are not equal, then aborts. +/// See `math.approxEqAbs` for more informations on the tolerance parameter. /// The types must be floating point -pub fn expectWithinMargin(expected: anytype, actual: @TypeOf(expected), margin: @TypeOf(expected)) void { - std.debug.assert(margin >= 0.0); +pub fn expectApproxEqAbs(expected: anytype, actual: @TypeOf(expected), tolerance: @TypeOf(expected)) void { + const T = @TypeOf(expected); + + switch (@typeInfo(T)) { + .Float => if (!math.approxEqAbs(T, expected, actual, tolerance)) + std.debug.panic("actual {}, not within absolute tolerance {} of expected {}", .{ actual, tolerance, expected }), + + .ComptimeFloat => @compileError("Cannot approximately compare two comptime_float values"), - switch (@typeInfo(@TypeOf(actual))) { - .Float, - .ComptimeFloat, - => { - if (@fabs(expected - actual) > margin) { - std.debug.panic("actual {}, not within margin {} of expected {}", .{ actual, margin, expected }); - } - }, else => @compileError("Unable to compare non floating point values"), } } -test "expectWithinMargin" { +test "expectApproxEqAbs" { inline for ([_]type{ f16, f32, f64, f128 }) |T| { const pos_x: T = 12.0; const pos_y: T = 12.06; const neg_x: T = -12.0; const neg_y: T = -12.06; - expectWithinMargin(pos_x, pos_y, 0.1); - expectWithinMargin(neg_x, neg_y, 0.1); + expectApproxEqAbs(pos_x, pos_y, 0.1); + expectApproxEqAbs(neg_x, neg_y, 0.1); } } -/// This function is intended to be used only in tests. When the actual value is not -/// within the epsilon of the expected value, -/// prints diagnostics to stderr to show exactly how they are not equal, then aborts. +/// This function is intended to be used only in tests. When the actual value is +/// not approximately equal to the expected value, prints diagnostics to stderr +/// to show exactly how they are not equal, then aborts. +/// See `math.approxEqRel` for more informations on the tolerance parameter. /// The types must be floating point -pub fn expectWithinEpsilon(expected: anytype, actual: @TypeOf(expected), epsilon: @TypeOf(expected)) void { - std.debug.assert(epsilon >= 0.0 and epsilon <= 1.0); +pub fn expectApproxEqRel(expected: anytype, actual: @TypeOf(expected), tolerance: @TypeOf(expected)) void { + const T = @TypeOf(expected); + + switch (@typeInfo(T)) { + .Float => if (!math.approxEqRel(T, expected, actual, tolerance)) + std.debug.panic("actual {}, not within relative tolerance {} of expected {}", .{ actual, tolerance, expected }), + + .ComptimeFloat => @compileError("Cannot approximately compare two comptime_float values"), - // Relative epsilon test. - const margin = math.max(math.fabs(expected), math.fabs(actual)) * epsilon; - switch (@typeInfo(@TypeOf(actual))) { - .Float, - .ComptimeFloat, - => { - if (@fabs(expected - actual) > margin) { - std.debug.panic("actual {}, not within epsilon {}, of expected {}", .{ actual, epsilon, expected }); - } - }, else => @compileError("Unable to compare non floating point values"), } } -test "expectWithinEpsilon" { +test "expectApproxEqRel" { inline for ([_]type{ f16, f32, f64, f128 }) |T| { + const eps_value = comptime math.epsilon(T); + const sqrt_eps_value = comptime math.sqrt(eps_value); + const pos_x: T = 12.0; - const pos_y: T = 13.2; + const pos_y: T = pos_x + 2 * eps_value; const neg_x: T = -12.0; - const neg_y: T = -13.2; + const neg_y: T = neg_x - 2 * eps_value; - expectWithinEpsilon(pos_x, pos_y, 0.1); - expectWithinEpsilon(neg_x, neg_y, 0.1); + expectApproxEqRel(pos_x, pos_y, sqrt_eps_value); + expectApproxEqRel(neg_x, neg_y, sqrt_eps_value); } } diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig index 5839376b5f..4b88ce020a 100644 --- a/test/stage1/behavior/vector.zig +++ b/test/stage1/behavior/vector.zig @@ -4,7 +4,7 @@ const mem = std.mem; const math = std.math; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; -const expectWithinEpsilon = std.testing.expectWithinEpsilon; +const expectApproxEqRel = std.testing.expectApproxEqRel; const Vector = std.meta.Vector; test "implicit cast vector to array - bool" { @@ -514,10 +514,14 @@ test "vector reduce operation" { switch (@typeInfo(TX)) { .Int, .Bool => expectEqual(expected, r), .Float => { - if (math.isNan(expected) != math.isNan(r)) { - std.debug.panic("unexpected NaN value!\n", .{}); + const expected_nan = math.isNan(expected); + const got_nan = math.isNan(r); + + if (expected_nan and got_nan) { + // Do this check explicitly as two NaN values are never + // equal. } else { - expectWithinEpsilon(expected, r, 0.001); + expectApproxEqRel(expected, r, math.sqrt(math.epsilon(TX))); } }, else => unreachable, |
