aboutsummaryrefslogtreecommitdiff
path: root/lib/std/math.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2024-03-13 15:56:09 -0700
committerAndrew Kelley <andrew@ziglang.org>2024-03-21 14:11:46 -0700
commit12191c8a220ca5594b278b5077bff98e8fecac08 (patch)
treece6552d68161c363dd18826220febbaa7f733c62 /lib/std/math.zig
parent5ae838d10596a272644da314937b01aa1a271a73 (diff)
downloadzig-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.zig160
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;