diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-03-13 15:56:09 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2024-03-21 14:11:46 -0700 |
| commit | 12191c8a220ca5594b278b5077bff98e8fecac08 (patch) | |
| tree | ce6552d68161c363dd18826220febbaa7f733c62 /lib/std/math.zig | |
| parent | 5ae838d10596a272644da314937b01aa1a271a73 (diff) | |
| download | zig-12191c8a220ca5594b278b5077bff98e8fecac08.tar.gz zig-12191c8a220ca5594b278b5077bff98e8fecac08.zip | |
std: promote tests to doctests
Now these show up as "example usage" in generated documentation.
Diffstat (limited to 'lib/std/math.zig')
| -rw-r--r-- | lib/std/math.zig | 160 |
1 files changed, 90 insertions, 70 deletions
diff --git a/lib/std/math.zig b/lib/std/math.zig index 403c19d20b..7524b8d2f5 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -167,28 +167,51 @@ pub fn approxEqRel(comptime T: type, x: T, y: T, tolerance: T) bool { return @abs(x - y) <= @max(@abs(x), @abs(y)) * tolerance; } -test "approxEqAbs and approxEqRel" { +test approxEqAbs { inline for ([_]type{ f16, f32, f64, f128 }) |T| { const eps_value = comptime floatEps(T); - const sqrt_eps_value = comptime sqrt(eps_value); - const nan_value = comptime nan(T); - const inf_value = comptime inf(T); const min_value = comptime floatMin(T); try testing.expect(approxEqAbs(T, 0.0, 0.0, eps_value)); try testing.expect(approxEqAbs(T, -0.0, -0.0, eps_value)); try testing.expect(approxEqAbs(T, 0.0, -0.0, eps_value)); - try testing.expect(approxEqRel(T, 1.0, 1.0, sqrt_eps_value)); - try testing.expect(!approxEqRel(T, 1.0, 0.0, sqrt_eps_value)); try testing.expect(!approxEqAbs(T, 1.0 + 2 * eps_value, 1.0, eps_value)); try testing.expect(approxEqAbs(T, 1.0 + 1 * eps_value, 1.0, eps_value)); + try testing.expect(approxEqAbs(T, min_value, 0.0, eps_value * 2)); + try testing.expect(approxEqAbs(T, -min_value, 0.0, eps_value * 2)); + } + + comptime { + // `comptime_float` is guaranteed to have the same precision and operations of + // the largest other floating point type, which is f128 but it doesn't have a + // defined layout so we can't rely on `@bitCast` to construct the smallest + // possible epsilon value like we do in the tests above. In the same vein, we + // also can't represent a max/min, `NaN` or `Inf` values. + const eps_value = 1e-4; + + try testing.expect(approxEqAbs(comptime_float, 0.0, 0.0, eps_value)); + try testing.expect(approxEqAbs(comptime_float, -0.0, -0.0, eps_value)); + try testing.expect(approxEqAbs(comptime_float, 0.0, -0.0, eps_value)); + try testing.expect(!approxEqAbs(comptime_float, 1.0 + 2 * eps_value, 1.0, eps_value)); + try testing.expect(approxEqAbs(comptime_float, 1.0 + 1 * eps_value, 1.0, eps_value)); + } +} + +test approxEqRel { + inline for ([_]type{ f16, f32, f64, f128 }) |T| { + const eps_value = comptime floatEps(T); + const sqrt_eps_value = comptime sqrt(eps_value); + const nan_value = comptime nan(T); + const inf_value = comptime inf(T); + const min_value = comptime floatMin(T); + + try testing.expect(approxEqRel(T, 1.0, 1.0, sqrt_eps_value)); + try testing.expect(!approxEqRel(T, 1.0, 0.0, sqrt_eps_value)); try testing.expect(!approxEqRel(T, 1.0, nan_value, sqrt_eps_value)); try testing.expect(!approxEqRel(T, nan_value, nan_value, sqrt_eps_value)); try testing.expect(approxEqRel(T, inf_value, inf_value, sqrt_eps_value)); try testing.expect(approxEqRel(T, min_value, min_value, sqrt_eps_value)); try testing.expect(approxEqRel(T, -min_value, -min_value, sqrt_eps_value)); - try testing.expect(approxEqAbs(T, min_value, 0.0, eps_value * 2)); - try testing.expect(approxEqAbs(T, -min_value, 0.0, eps_value * 2)); } comptime { @@ -200,13 +223,8 @@ test "approxEqAbs and approxEqRel" { const eps_value = 1e-4; const sqrt_eps_value = sqrt(eps_value); - try testing.expect(approxEqAbs(comptime_float, 0.0, 0.0, eps_value)); - try testing.expect(approxEqAbs(comptime_float, -0.0, -0.0, eps_value)); - try testing.expect(approxEqAbs(comptime_float, 0.0, -0.0, eps_value)); try testing.expect(approxEqRel(comptime_float, 1.0, 1.0, sqrt_eps_value)); try testing.expect(!approxEqRel(comptime_float, 1.0, 0.0, sqrt_eps_value)); - try testing.expect(!approxEqAbs(comptime_float, 1.0 + 2 * eps_value, 1.0, eps_value)); - try testing.expect(approxEqAbs(comptime_float, 1.0 + 1 * eps_value, 1.0, eps_value)); } } @@ -310,7 +328,7 @@ pub fn radiansToDegrees(ang: anytype) if (@TypeOf(ang) == comptime_int) comptime @compileError("Input must be float or a comptime number, or a vector of floats."); } -test "radiansToDegrees" { +test radiansToDegrees { const zero: f32 = 0; const half_pi: f32 = pi / 2.0; const neg_quart_pi: f32 = -pi / 4.0; @@ -345,7 +363,7 @@ pub fn degreesToRadians(ang: anytype) if (@TypeOf(ang) == comptime_int) comptime @compileError("Input must be float or a comptime number, or a vector of floats."); } -test "degreesToRadians" { +test degreesToRadians { const ninety: f32 = 90; const neg_two_seventy: f32 = -270; const three_sixty: f32 = 360; @@ -506,7 +524,7 @@ pub fn wrap(x: anytype, r: anytype) @TypeOf(x) { }, } } -test "wrap" { +test wrap { // Within range try testing.expect(wrap(@as(i32, -75), @as(i32, 180)) == -75); try testing.expect(wrap(@as(i32, -75), @as(i32, -180)) == -75); @@ -543,8 +561,7 @@ test "wrap" { var i: i32 = 1; _ = &i; try testing.expect(wrap(i, 10) == 1); -} -test wrap { + const limit: i32 = 180; // Within range try testing.expect(wrap(@as(i32, -75), limit) == -75); @@ -569,7 +586,7 @@ pub fn clamp(val: anytype, lower: anytype, upper: anytype) @TypeOf(val, lower, u assert(lower <= upper); return @max(lower, @min(val, upper)); } -test "clamp" { +test clamp { // Within range try testing.expect(std.math.clamp(@as(i32, -1), @as(i32, -4), @as(i32, 7)) == -1); // Below @@ -650,7 +667,7 @@ pub fn shl(comptime T: type, a: T, shift_amt: anytype) T { return a << casted_shift_amt; } -test "shl" { +test shl { if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { // https://github.com/ziglang/zig/issues/12012 return error.SkipZigTest; @@ -695,7 +712,7 @@ pub fn shr(comptime T: type, a: T, shift_amt: anytype) T { return a >> casted_shift_amt; } -test "shr" { +test shr { if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { // https://github.com/ziglang/zig/issues/12012 return error.SkipZigTest; @@ -741,7 +758,7 @@ pub fn rotr(comptime T: type, x: T, r: anytype) T { } } -test "rotr" { +test rotr { if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { // https://github.com/ziglang/zig/issues/12012 return error.SkipZigTest; @@ -787,7 +804,7 @@ pub fn rotl(comptime T: type, x: T, r: anytype) T { } } -test "rotl" { +test rotl { if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { // https://github.com/ziglang/zig/issues/12012 return error.SkipZigTest; @@ -850,7 +867,7 @@ pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) t return std.meta.Int(signedness, magnitude_bits); } -test "IntFittingRange" { +test IntFittingRange { try testing.expect(IntFittingRange(0, 0) == u0); try testing.expect(IntFittingRange(0, 1) == u1); try testing.expect(IntFittingRange(0, 2) == u2); @@ -918,7 +935,7 @@ pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T { return @divTrunc(numerator, denominator); } -test "divTrunc" { +test divTrunc { try testDivTrunc(); try comptime testDivTrunc(); } @@ -942,7 +959,7 @@ pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T { return @divFloor(numerator, denominator); } -test "divFloor" { +test divFloor { try testDivFloor(); try comptime testDivFloor(); } @@ -979,7 +996,7 @@ pub fn divCeil(comptime T: type, numerator: T, denominator: T) !T { } } -test "divCeil" { +test divCeil { try testDivCeil(); try comptime testDivCeil(); } @@ -1023,7 +1040,7 @@ pub fn divExact(comptime T: type, numerator: T, denominator: T) !T { return result; } -test "divExact" { +test divExact { try testDivExact(); try comptime testDivExact(); } @@ -1049,7 +1066,7 @@ pub fn mod(comptime T: type, numerator: T, denominator: T) !T { return @mod(numerator, denominator); } -test "mod" { +test mod { try testMod(); try comptime testMod(); } @@ -1075,7 +1092,7 @@ pub fn rem(comptime T: type, numerator: T, denominator: T) !T { return @rem(numerator, denominator); } -test "rem" { +test rem { try testRem(); try comptime testRem(); } @@ -1104,7 +1121,7 @@ pub fn negateCast(x: anytype) !std.meta.Int(.signed, @bitSizeOf(@TypeOf(x))) { return -@as(int, @intCast(x)); } -test "negateCast" { +test negateCast { try testing.expect((negateCast(@as(u32, 999)) catch unreachable) == -999); try testing.expect(@TypeOf(negateCast(@as(u32, 999)) catch unreachable) == i32); @@ -1129,7 +1146,7 @@ pub fn cast(comptime T: type, x: anytype) ?T { } } -test "cast" { +test cast { try testing.expect(cast(u8, 300) == null); try testing.expect(cast(u8, @as(u32, 300)) == null); try testing.expect(cast(i8, -200) == null); @@ -1188,7 +1205,7 @@ pub fn ByteAlignedInt(comptime T: type) type { return extended_type; } -test "ByteAlignedInt" { +test ByteAlignedInt { try testing.expect(ByteAlignedInt(u0) == u0); try testing.expect(ByteAlignedInt(i0) == i0); try testing.expect(ByteAlignedInt(u3) == u8); @@ -1226,7 +1243,7 @@ pub fn floorPowerOfTwo(comptime T: type, value: T) T { return @as(T, 1) << log2_int(uT, @as(uT, @intCast(value))); } -test "floorPowerOfTwo" { +test floorPowerOfTwo { try testFloorPowerOfTwo(); try comptime testFloorPowerOfTwo(); } @@ -1288,7 +1305,7 @@ pub fn ceilPowerOfTwoAssert(comptime T: type, value: T) T { return ceilPowerOfTwo(T, value) catch unreachable; } -test "ceilPowerOfTwoPromote" { +test ceilPowerOfTwoPromote { try testCeilPowerOfTwoPromote(); try comptime testCeilPowerOfTwoPromote(); } @@ -1305,7 +1322,7 @@ fn testCeilPowerOfTwoPromote() !void { try testing.expectEqual(@as(u5, 16), ceilPowerOfTwoPromote(u4, 9)); } -test "ceilPowerOfTwo" { +test ceilPowerOfTwo { try testCeilPowerOfTwo(); try comptime testCeilPowerOfTwo(); } @@ -1398,7 +1415,7 @@ pub fn lossyCast(comptime T: type, value: anytype) T { } } -test "lossyCast" { +test lossyCast { try testing.expect(lossyCast(i16, 70000.0) == @as(i16, 32767)); try testing.expect(lossyCast(u32, @as(i16, -255)) == @as(u32, 0)); try testing.expect(lossyCast(i9, @as(u32, 200)) == @as(i9, 200)); @@ -1417,7 +1434,7 @@ pub fn lerp(a: anytype, b: anytype, t: anytype) @TypeOf(a, b, t) { return @mulAdd(Type, b - a, t, a); } -test "lerp" { +test lerp { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/17884 if (builtin.zig_backend == .stage2_x86_64 and !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .fma)) return error.SkipZigTest; @@ -1483,7 +1500,7 @@ pub fn minInt(comptime T: type) comptime_int { return -(1 << (bit_count - 1)); } -test "minInt and maxInt" { +test maxInt { try testing.expect(maxInt(u0) == 0); try testing.expect(maxInt(u1) == 1); try testing.expect(maxInt(u8) == 255); @@ -1500,7 +1517,9 @@ test "minInt and maxInt" { try testing.expect(maxInt(i63) == 4611686018427387903); try testing.expect(maxInt(i64) == 9223372036854775807); try testing.expect(maxInt(i128) == 170141183460469231731687303715884105727); +} +test minInt { try testing.expect(minInt(u0) == 0); try testing.expect(minInt(u1) == 0); try testing.expect(minInt(u8) == 0); @@ -1538,7 +1557,7 @@ pub fn mulWide(comptime T: type, a: T, b: T) std.meta.Int( return @as(ResultInt, a) * @as(ResultInt, b); } -test "mulWide" { +test mulWide { try testing.expect(mulWide(u8, 5, 5) == 25); try testing.expect(mulWide(i8, 5, -5) == -25); try testing.expect(mulWide(u8, 100, 100) == 10000); @@ -1563,6 +1582,12 @@ pub const Order = enum { }; } + test invert { + try testing.expect(Order.invert(order(0, 0)) == .eq); + try testing.expect(Order.invert(order(1, 0)) == .lt); + try testing.expect(Order.invert(order(-1, 0)) == .gt); + } + pub fn compare(self: Order, op: CompareOperator) bool { return switch (self) { .lt => switch (op) { @@ -1591,6 +1616,18 @@ pub const Order = enum { }, }; } + + // https://github.com/ziglang/zig/issues/19295 + test "compare" { + try testing.expect(order(-1, 0).compare(.lt)); + try testing.expect(order(-1, 0).compare(.lte)); + try testing.expect(order(0, 0).compare(.lte)); + try testing.expect(order(0, 0).compare(.eq)); + try testing.expect(order(0, 0).compare(.gte)); + try testing.expect(order(1, 0).compare(.gte)); + try testing.expect(order(1, 0).compare(.gt)); + try testing.expect(order(1, 0).compare(.neq)); + } }; /// Given two numbers, this function returns the order they are with respect to each other. @@ -1633,6 +1670,15 @@ pub const CompareOperator = enum { .neq => .neq, }; } + + test reverse { + inline for (@typeInfo(CompareOperator).Enum.fields) |op_field| { + const op = @as(CompareOperator, @enumFromInt(op_field.value)); + try testing.expect(compare(2, op, 3) == compare(3, op.reverse(), 2)); + try testing.expect(compare(3, op, 3) == compare(3, op.reverse(), 3)); + try testing.expect(compare(4, op, 3) == compare(3, op.reverse(), 4)); + } + } }; /// This function does the same thing as comparison operators, however the @@ -1649,7 +1695,7 @@ pub fn compare(a: anytype, op: CompareOperator, b: anytype) bool { }; } -test "compare between signed and unsigned" { +test compare { try testing.expect(compare(@as(i8, -1), .lt, @as(u8, 255))); try testing.expect(compare(@as(i8, 2), .gt, @as(u8, 1))); try testing.expect(!compare(@as(i8, -1), .gte, @as(u8, 255))); @@ -1669,38 +1715,12 @@ test "compare between signed and unsigned" { try testing.expect(compare(@as(u8, 1), .eq, @as(u8, 1))); } -test "order" { +test order { try testing.expect(order(0, 0) == .eq); try testing.expect(order(1, 0) == .gt); try testing.expect(order(-1, 0) == .lt); } -test "order.invert" { - try testing.expect(Order.invert(order(0, 0)) == .eq); - try testing.expect(Order.invert(order(1, 0)) == .lt); - try testing.expect(Order.invert(order(-1, 0)) == .gt); -} - -test "order.compare" { - try testing.expect(order(-1, 0).compare(.lt)); - try testing.expect(order(-1, 0).compare(.lte)); - try testing.expect(order(0, 0).compare(.lte)); - try testing.expect(order(0, 0).compare(.eq)); - try testing.expect(order(0, 0).compare(.gte)); - try testing.expect(order(1, 0).compare(.gte)); - try testing.expect(order(1, 0).compare(.gt)); - try testing.expect(order(1, 0).compare(.neq)); -} - -test "compare.reverse" { - inline for (@typeInfo(CompareOperator).Enum.fields) |op_field| { - const op = @as(CompareOperator, @enumFromInt(op_field.value)); - try testing.expect(compare(2, op, 3) == compare(3, op.reverse(), 2)); - try testing.expect(compare(3, op, 3) == compare(3, op.reverse(), 3)); - try testing.expect(compare(4, op, 3) == compare(3, op.reverse(), 4)); - } -} - /// Returns a mask of all ones if value is true, /// and a mask of all zeroes if value is false. /// Compiles to one instruction for register sized integers. @@ -1722,7 +1742,7 @@ pub inline fn boolMask(comptime MaskInt: type, value: bool) MaskInt { return -%@as(MaskInt, @intCast(@intFromBool(value))); } -test "boolMask" { +test boolMask { const runTest = struct { fn runTest() !void { try testing.expectEqual(@as(u1, 0), boolMask(u1, false)); @@ -1870,7 +1890,7 @@ fn testSign() !void { try std.testing.expectEqual(0.0, sign(0.0)); } -test "sign" { +test sign { if (builtin.zig_backend == .stage2_llvm) { // https://github.com/ziglang/zig/issues/12012 return error.SkipZigTest; |
