authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2021-03-13 13:08:46+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-14 17:23:47-04:00
log27d07c6c4de36af1186392d4bec321825403860e
tree39a45001ab3c67640e112fed1a61342ff54101f7
parentd33f0d3375c621db479fbf565855c7d2a7eea735

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.

3 files changed, 47 insertions(+), 41 deletions(-)

lib/std/os/linux/io_uring.zig+1-1
......@@ -1353,7 +1353,7 @@ test "timeout (after a relative time)" {
13531353 .res = -linux.ETIME,
13541354 .flags = 0,
13551355 }, cqe);
1356 testing.expectWithinMargin(@intToFloat(f64, ms), @intToFloat(f64, stopped - started), margin);
1356 testing.expectApproxEqAbs(@intToFloat(f64, ms), @intToFloat(f64, stopped - started), margin);
13571357}
13581358
13591359test "timeout (after a number of completions)" {
lib/std/testing.zig+38-36
......@@ -200,67 +200,69 @@ pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anyt
200200 return error.TestFailed;
201201}
202202
203/// This function is intended to be used only in tests. When the actual value is not
204/// within the margin of the expected value,
205/// prints diagnostics to stderr to show exactly how they are not equal, then aborts.
203pub const expectWithinMargin = @compileError("expectWithinMargin is deprecated, use expectApproxEqAbs or expectApproxEqRel");
204pub const expectWithinEpsilon = @compileError("expectWithinEpsilon is deprecated, use expectApproxEqAbs or expectApproxEqRel");
205
206/// This function is intended to be used only in tests. When the actual value is
207/// not approximately equal to the expected value, prints diagnostics to stderr
208/// to show exactly how they are not equal, then aborts.
209/// See `math.approxEqAbs` for more informations on the tolerance parameter.
206210/// The types must be floating point
207pub fn expectWithinMargin(expected: anytype, actual: @TypeOf(expected), margin: @TypeOf(expected)) void {
208 std.debug.assert(margin >= 0.0);
211pub fn expectApproxEqAbs(expected: anytype, actual: @TypeOf(expected), tolerance: @TypeOf(expected)) void {
212 const T = @TypeOf(expected);
213
214 switch (@typeInfo(T)) {
215 .Float => if (!math.approxEqAbs(T, expected, actual, tolerance))
216 std.debug.panic("actual {}, not within absolute tolerance {} of expected {}", .{ actual, tolerance, expected }),
217
218 .ComptimeFloat => @compileError("Cannot approximately compare two comptime_float values"),
209219
210 switch (@typeInfo(@TypeOf(actual))) {
211 .Float,
212 .ComptimeFloat,
213 => {
214 if (@fabs(expected - actual) > margin) {
215 std.debug.panic("actual {}, not within margin {} of expected {}", .{ actual, margin, expected });
216 }
217 },
218220 else => @compileError("Unable to compare non floating point values"),
219221 }
220222}
221223
222test "expectWithinMargin" {
224test "expectApproxEqAbs" {
223225 inline for ([_]type{ f16, f32, f64, f128 }) |T| {
224226 const pos_x: T = 12.0;
225227 const pos_y: T = 12.06;
226228 const neg_x: T = -12.0;
227229 const neg_y: T = -12.06;
228230
229 expectWithinMargin(pos_x, pos_y, 0.1);
230 expectWithinMargin(neg_x, neg_y, 0.1);
231 expectApproxEqAbs(pos_x, pos_y, 0.1);
232 expectApproxEqAbs(neg_x, neg_y, 0.1);
231233 }
232234}
233235
234/// This function is intended to be used only in tests. When the actual value is not
235/// within the epsilon of the expected value,
236/// prints diagnostics to stderr to show exactly how they are not equal, then aborts.
236/// This function is intended to be used only in tests. When the actual value is
237/// not approximately equal to the expected value, prints diagnostics to stderr
238/// to show exactly how they are not equal, then aborts.
239/// See `math.approxEqRel` for more informations on the tolerance parameter.
237240/// The types must be floating point
238pub fn expectWithinEpsilon(expected: anytype, actual: @TypeOf(expected), epsilon: @TypeOf(expected)) void {
239 std.debug.assert(epsilon >= 0.0 and epsilon <= 1.0);
241pub fn expectApproxEqRel(expected: anytype, actual: @TypeOf(expected), tolerance: @TypeOf(expected)) void {
242 const T = @TypeOf(expected);
243
244 switch (@typeInfo(T)) {
245 .Float => if (!math.approxEqRel(T, expected, actual, tolerance))
246 std.debug.panic("actual {}, not within relative tolerance {} of expected {}", .{ actual, tolerance, expected }),
247
248 .ComptimeFloat => @compileError("Cannot approximately compare two comptime_float values"),
240249
241 // Relative epsilon test.
242 const margin = math.max(math.fabs(expected), math.fabs(actual)) * epsilon;
243 switch (@typeInfo(@TypeOf(actual))) {
244 .Float,
245 .ComptimeFloat,
246 => {
247 if (@fabs(expected - actual) > margin) {
248 std.debug.panic("actual {}, not within epsilon {}, of expected {}", .{ actual, epsilon, expected });
249 }
250 },
251250 else => @compileError("Unable to compare non floating point values"),
252251 }
253252}
254253
255test "expectWithinEpsilon" {
254test "expectApproxEqRel" {
256255 inline for ([_]type{ f16, f32, f64, f128 }) |T| {
256 const eps_value = comptime math.epsilon(T);
257 const sqrt_eps_value = comptime math.sqrt(eps_value);
258
257259 const pos_x: T = 12.0;
258 const pos_y: T = 13.2;
260 const pos_y: T = pos_x + 2 * eps_value;
259261 const neg_x: T = -12.0;
260 const neg_y: T = -13.2;
262 const neg_y: T = neg_x - 2 * eps_value;
261263
262 expectWithinEpsilon(pos_x, pos_y, 0.1);
263 expectWithinEpsilon(neg_x, neg_y, 0.1);
264 expectApproxEqRel(pos_x, pos_y, sqrt_eps_value);
265 expectApproxEqRel(neg_x, neg_y, sqrt_eps_value);
264266 }
265267}
266268
test/stage1/behavior/vector.zig+8-4
......@@ -4,7 +4,7 @@ const mem = std.mem;
44const math = std.math;
55const expect = std.testing.expect;
66const expectEqual = std.testing.expectEqual;
7const expectWithinEpsilon = std.testing.expectWithinEpsilon;
7const expectApproxEqRel = std.testing.expectApproxEqRel;
88const Vector = std.meta.Vector;
99
1010test "implicit cast vector to array - bool" {
......@@ -514,10 +514,14 @@ test "vector reduce operation" {
514514 switch (@typeInfo(TX)) {
515515 .Int, .Bool => expectEqual(expected, r),
516516 .Float => {
517 if (math.isNan(expected) != math.isNan(r)) {
518 std.debug.panic("unexpected NaN value!\n", .{});
517 const expected_nan = math.isNan(expected);
518 const got_nan = math.isNan(r);
519
520 if (expected_nan and got_nan) {
521 // Do this check explicitly as two NaN values are never
522 // equal.
519523 } else {
520 expectWithinEpsilon(expected, r, 0.001);
524 expectApproxEqRel(expected, r, math.sqrt(math.epsilon(TX)));
521525 }
522526 },
523527 else => unreachable,