Commit ff14451b4a

LemonBoy <thatlemon@gmail.com>
2020-11-02 13:25:50
std: Implement more useful approxEq semantics
Comparisons with absolute epsilons are usually useful when comparing numbers to zero, for non-zero numbers it's advised to switch to relative epsilons instead to obtain meaningful results (check [1] for more details). The new API introduces approxEqAbs and approxEqRel, where the former aliases and deprecated the old `approxEq`, allowing the user to pick the right tool for the job. The documentation is meant to guide the user in the choice of the correct alternative. [1] https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
1 parent 17837af
lib/std/fmt/parse_float.zig
@@ -370,7 +370,7 @@ test "fmt.parseFloat" {
     const testing = std.testing;
     const expect = testing.expect;
     const expectEqual = testing.expectEqual;
-    const approxEq = std.math.approxEq;
+    const approxEqAbs = std.math.approxEqAbs;
     const epsilon = 1e-7;
 
     inline for ([_]type{ f16, f32, f64, f128 }) |T| {
@@ -392,8 +392,8 @@ test "fmt.parseFloat" {
         expectEqual(try parseFloat(T, "-1e0"), -1.0);
         expectEqual(try parseFloat(T, "1.234e3"), 1234);
 
-        expect(approxEq(T, try parseFloat(T, "3.141"), 3.141, epsilon));
-        expect(approxEq(T, try parseFloat(T, "-3.141"), -3.141, epsilon));
+        expect(approxEqAbs(T, try parseFloat(T, "3.141"), 3.141, epsilon));
+        expect(approxEqAbs(T, try parseFloat(T, "-3.141"), -3.141, epsilon));
 
         expectEqual(try parseFloat(T, "1e-700"), 0);
         expectEqual(try parseFloat(T, "1e+700"), std.math.inf(T));
@@ -405,13 +405,13 @@ test "fmt.parseFloat" {
         expectEqual(try parseFloat(T, "0.4e0066999999999999999999999999999999999999999999999999999"), std.math.inf(T));
 
         if (T != f16) {
-            expect(approxEq(T, try parseFloat(T, "1e-2"), 0.01, epsilon));
-            expect(approxEq(T, try parseFloat(T, "1234e-2"), 12.34, epsilon));
+            expect(approxEqAbs(T, try parseFloat(T, "1e-2"), 0.01, epsilon));
+            expect(approxEqAbs(T, try parseFloat(T, "1234e-2"), 12.34, epsilon));
 
-            expect(approxEq(T, try parseFloat(T, "123142.1"), 123142.1, epsilon));
-            expect(approxEq(T, try parseFloat(T, "-123142.1124"), @as(T, -123142.1124), epsilon));
-            expect(approxEq(T, try parseFloat(T, "0.7062146892655368"), @as(T, 0.7062146892655368), epsilon));
-            expect(approxEq(T, try parseFloat(T, "2.71828182845904523536"), @as(T, 2.718281828459045), epsilon));
+            expect(approxEqAbs(T, try parseFloat(T, "123142.1"), 123142.1, epsilon));
+            expect(approxEqAbs(T, try parseFloat(T, "-123142.1124"), @as(T, -123142.1124), epsilon));
+            expect(approxEqAbs(T, try parseFloat(T, "0.7062146892655368"), @as(T, 0.7062146892655368), epsilon));
+            expect(approxEqAbs(T, try parseFloat(T, "2.71828182845904523536"), @as(T, 2.718281828459045), epsilon));
         }
     }
 }
lib/std/math/complex/abs.zig
@@ -20,5 +20,5 @@ const epsilon = 0.0001;
 test "complex.cabs" {
     const a = Complex(f32).new(5, 3);
     const c = abs(a);
-    testing.expect(math.approxEq(f32, c, 5.83095, epsilon));
+    testing.expect(math.approxEqAbs(f32, c, 5.83095, epsilon));
 }
lib/std/math/complex/acos.zig
@@ -22,6 +22,6 @@ test "complex.cacos" {
     const a = Complex(f32).new(5, 3);
     const c = acos(a);
 
-    testing.expect(math.approxEq(f32, c.re, 0.546975, epsilon));
-    testing.expect(math.approxEq(f32, c.im, -2.452914, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, 0.546975, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, -2.452914, epsilon));
 }
lib/std/math/complex/acosh.zig
@@ -22,6 +22,6 @@ test "complex.cacosh" {
     const a = Complex(f32).new(5, 3);
     const c = acosh(a);
 
-    testing.expect(math.approxEq(f32, c.re, 2.452914, epsilon));
-    testing.expect(math.approxEq(f32, c.im, 0.546975, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, 2.452914, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, 0.546975, epsilon));
 }
lib/std/math/complex/arg.zig
@@ -20,5 +20,5 @@ const epsilon = 0.0001;
 test "complex.carg" {
     const a = Complex(f32).new(5, 3);
     const c = arg(a);
-    testing.expect(math.approxEq(f32, c, 0.540420, epsilon));
+    testing.expect(math.approxEqAbs(f32, c, 0.540420, epsilon));
 }
lib/std/math/complex/asin.zig
@@ -28,6 +28,6 @@ test "complex.casin" {
     const a = Complex(f32).new(5, 3);
     const c = asin(a);
 
-    testing.expect(math.approxEq(f32, c.re, 1.023822, epsilon));
-    testing.expect(math.approxEq(f32, c.im, 2.452914, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, 1.023822, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, 2.452914, epsilon));
 }
lib/std/math/complex/asinh.zig
@@ -23,6 +23,6 @@ test "complex.casinh" {
     const a = Complex(f32).new(5, 3);
     const c = asinh(a);
 
-    testing.expect(math.approxEq(f32, c.re, 2.459831, epsilon));
-    testing.expect(math.approxEq(f32, c.im, 0.533999, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, 2.459831, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, 0.533999, epsilon));
 }
lib/std/math/complex/atan.zig
@@ -130,14 +130,14 @@ test "complex.catan32" {
     const a = Complex(f32).new(5, 3);
     const c = atan(a);
 
-    testing.expect(math.approxEq(f32, c.re, 1.423679, epsilon));
-    testing.expect(math.approxEq(f32, c.im, 0.086569, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, 1.423679, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, 0.086569, epsilon));
 }
 
 test "complex.catan64" {
     const a = Complex(f64).new(5, 3);
     const c = atan(a);
 
-    testing.expect(math.approxEq(f64, c.re, 1.423679, epsilon));
-    testing.expect(math.approxEq(f64, c.im, 0.086569, epsilon));
+    testing.expect(math.approxEqAbs(f64, c.re, 1.423679, epsilon));
+    testing.expect(math.approxEqAbs(f64, c.im, 0.086569, epsilon));
 }
lib/std/math/complex/atanh.zig
@@ -23,6 +23,6 @@ test "complex.catanh" {
     const a = Complex(f32).new(5, 3);
     const c = atanh(a);
 
-    testing.expect(math.approxEq(f32, c.re, 0.146947, epsilon));
-    testing.expect(math.approxEq(f32, c.im, 1.480870, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, 0.146947, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, 1.480870, epsilon));
 }
lib/std/math/complex/cos.zig
@@ -22,6 +22,6 @@ test "complex.ccos" {
     const a = Complex(f32).new(5, 3);
     const c = cos(a);
 
-    testing.expect(math.approxEq(f32, c.re, 2.855815, epsilon));
-    testing.expect(math.approxEq(f32, c.im, 9.606383, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, 2.855815, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, 9.606383, epsilon));
 }
lib/std/math/complex/cosh.zig
@@ -165,14 +165,14 @@ test "complex.ccosh32" {
     const a = Complex(f32).new(5, 3);
     const c = cosh(a);
 
-    testing.expect(math.approxEq(f32, c.re, -73.467300, epsilon));
-    testing.expect(math.approxEq(f32, c.im, 10.471557, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, -73.467300, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, 10.471557, epsilon));
 }
 
 test "complex.ccosh64" {
     const a = Complex(f64).new(5, 3);
     const c = cosh(a);
 
-    testing.expect(math.approxEq(f64, c.re, -73.467300, epsilon));
-    testing.expect(math.approxEq(f64, c.im, 10.471557, epsilon));
+    testing.expect(math.approxEqAbs(f64, c.re, -73.467300, epsilon));
+    testing.expect(math.approxEqAbs(f64, c.im, 10.471557, epsilon));
 }
lib/std/math/complex/exp.zig
@@ -131,14 +131,14 @@ test "complex.cexp32" {
     const a = Complex(f32).new(5, 3);
     const c = exp(a);
 
-    testing.expect(math.approxEq(f32, c.re, -146.927917, epsilon));
-    testing.expect(math.approxEq(f32, c.im, 20.944065, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, -146.927917, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, 20.944065, epsilon));
 }
 
 test "complex.cexp64" {
     const a = Complex(f64).new(5, 3);
     const c = exp(a);
 
-    testing.expect(math.approxEq(f64, c.re, -146.927917, epsilon));
-    testing.expect(math.approxEq(f64, c.im, 20.944065, epsilon));
+    testing.expect(math.approxEqAbs(f64, c.re, -146.927917, epsilon));
+    testing.expect(math.approxEqAbs(f64, c.im, 20.944065, epsilon));
 }
lib/std/math/complex/log.zig
@@ -24,6 +24,6 @@ test "complex.clog" {
     const a = Complex(f32).new(5, 3);
     const c = log(a);
 
-    testing.expect(math.approxEq(f32, c.re, 1.763180, epsilon));
-    testing.expect(math.approxEq(f32, c.im, 0.540419, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, 1.763180, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, 0.540419, epsilon));
 }
lib/std/math/complex/pow.zig
@@ -23,6 +23,6 @@ test "complex.cpow" {
     const b = Complex(f32).new(2.3, -1.3);
     const c = pow(Complex(f32), a, b);
 
-    testing.expect(math.approxEq(f32, c.re, 58.049110, epsilon));
-    testing.expect(math.approxEq(f32, c.im, -101.003433, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, 58.049110, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, -101.003433, epsilon));
 }
lib/std/math/complex/sin.zig
@@ -23,6 +23,6 @@ test "complex.csin" {
     const a = Complex(f32).new(5, 3);
     const c = sin(a);
 
-    testing.expect(math.approxEq(f32, c.re, -9.654126, epsilon));
-    testing.expect(math.approxEq(f32, c.im, 2.841692, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, -9.654126, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, 2.841692, epsilon));
 }
lib/std/math/complex/sinh.zig
@@ -164,14 +164,14 @@ test "complex.csinh32" {
     const a = Complex(f32).new(5, 3);
     const c = sinh(a);
 
-    testing.expect(math.approxEq(f32, c.re, -73.460617, epsilon));
-    testing.expect(math.approxEq(f32, c.im, 10.472508, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, -73.460617, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, 10.472508, epsilon));
 }
 
 test "complex.csinh64" {
     const a = Complex(f64).new(5, 3);
     const c = sinh(a);
 
-    testing.expect(math.approxEq(f64, c.re, -73.460617, epsilon));
-    testing.expect(math.approxEq(f64, c.im, 10.472508, epsilon));
+    testing.expect(math.approxEqAbs(f64, c.re, -73.460617, epsilon));
+    testing.expect(math.approxEqAbs(f64, c.im, 10.472508, epsilon));
 }
lib/std/math/complex/sqrt.zig
@@ -138,14 +138,14 @@ test "complex.csqrt32" {
     const a = Complex(f32).new(5, 3);
     const c = sqrt(a);
 
-    testing.expect(math.approxEq(f32, c.re, 2.327117, epsilon));
-    testing.expect(math.approxEq(f32, c.im, 0.644574, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, 2.327117, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, 0.644574, epsilon));
 }
 
 test "complex.csqrt64" {
     const a = Complex(f64).new(5, 3);
     const c = sqrt(a);
 
-    testing.expect(math.approxEq(f64, c.re, 2.3271175190399496, epsilon));
-    testing.expect(math.approxEq(f64, c.im, 0.6445742373246469, epsilon));
+    testing.expect(math.approxEqAbs(f64, c.re, 2.3271175190399496, epsilon));
+    testing.expect(math.approxEqAbs(f64, c.im, 0.6445742373246469, epsilon));
 }
lib/std/math/complex/tan.zig
@@ -23,6 +23,6 @@ test "complex.ctan" {
     const a = Complex(f32).new(5, 3);
     const c = tan(a);
 
-    testing.expect(math.approxEq(f32, c.re, -0.002708233, epsilon));
-    testing.expect(math.approxEq(f32, c.im, 1.004165, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, -0.002708233, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, 1.004165, epsilon));
 }
lib/std/math/complex/tanh.zig
@@ -113,14 +113,14 @@ test "complex.ctanh32" {
     const a = Complex(f32).new(5, 3);
     const c = tanh(a);
 
-    testing.expect(math.approxEq(f32, c.re, 0.999913, epsilon));
-    testing.expect(math.approxEq(f32, c.im, -0.000025, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, 0.999913, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.im, -0.000025, epsilon));
 }
 
 test "complex.ctanh64" {
     const a = Complex(f64).new(5, 3);
     const c = tanh(a);
 
-    testing.expect(math.approxEq(f64, c.re, 0.999913, epsilon));
-    testing.expect(math.approxEq(f64, c.im, -0.000025, epsilon));
+    testing.expect(math.approxEqAbs(f64, c.re, 0.999913, epsilon));
+    testing.expect(math.approxEqAbs(f64, c.im, -0.000025, epsilon));
 }
lib/std/math/acos.zig
@@ -161,23 +161,23 @@ test "math.acos" {
 test "math.acos32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, acos32(0.0), 1.570796, epsilon));
-    expect(math.approxEq(f32, acos32(0.2), 1.369438, epsilon));
-    expect(math.approxEq(f32, acos32(0.3434), 1.220262, epsilon));
-    expect(math.approxEq(f32, acos32(0.5), 1.047198, epsilon));
-    expect(math.approxEq(f32, acos32(0.8923), 0.468382, epsilon));
-    expect(math.approxEq(f32, acos32(-0.2), 1.772154, epsilon));
+    expect(math.approxEqAbs(f32, acos32(0.0), 1.570796, epsilon));
+    expect(math.approxEqAbs(f32, acos32(0.2), 1.369438, epsilon));
+    expect(math.approxEqAbs(f32, acos32(0.3434), 1.220262, epsilon));
+    expect(math.approxEqAbs(f32, acos32(0.5), 1.047198, epsilon));
+    expect(math.approxEqAbs(f32, acos32(0.8923), 0.468382, epsilon));
+    expect(math.approxEqAbs(f32, acos32(-0.2), 1.772154, epsilon));
 }
 
 test "math.acos64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, acos64(0.0), 1.570796, epsilon));
-    expect(math.approxEq(f64, acos64(0.2), 1.369438, epsilon));
-    expect(math.approxEq(f64, acos64(0.3434), 1.220262, epsilon));
-    expect(math.approxEq(f64, acos64(0.5), 1.047198, epsilon));
-    expect(math.approxEq(f64, acos64(0.8923), 0.468382, epsilon));
-    expect(math.approxEq(f64, acos64(-0.2), 1.772154, epsilon));
+    expect(math.approxEqAbs(f64, acos64(0.0), 1.570796, epsilon));
+    expect(math.approxEqAbs(f64, acos64(0.2), 1.369438, epsilon));
+    expect(math.approxEqAbs(f64, acos64(0.3434), 1.220262, epsilon));
+    expect(math.approxEqAbs(f64, acos64(0.5), 1.047198, epsilon));
+    expect(math.approxEqAbs(f64, acos64(0.8923), 0.468382, epsilon));
+    expect(math.approxEqAbs(f64, acos64(-0.2), 1.772154, epsilon));
 }
 
 test "math.acos32.special" {
lib/std/math/acosh.zig
@@ -73,19 +73,19 @@ test "math.acosh" {
 test "math.acosh32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, acosh32(1.5), 0.962424, epsilon));
-    expect(math.approxEq(f32, acosh32(37.45), 4.315976, epsilon));
-    expect(math.approxEq(f32, acosh32(89.123), 5.183133, epsilon));
-    expect(math.approxEq(f32, acosh32(123123.234375), 12.414088, epsilon));
+    expect(math.approxEqAbs(f32, acosh32(1.5), 0.962424, epsilon));
+    expect(math.approxEqAbs(f32, acosh32(37.45), 4.315976, epsilon));
+    expect(math.approxEqAbs(f32, acosh32(89.123), 5.183133, epsilon));
+    expect(math.approxEqAbs(f32, acosh32(123123.234375), 12.414088, epsilon));
 }
 
 test "math.acosh64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, acosh64(1.5), 0.962424, epsilon));
-    expect(math.approxEq(f64, acosh64(37.45), 4.315976, epsilon));
-    expect(math.approxEq(f64, acosh64(89.123), 5.183133, epsilon));
-    expect(math.approxEq(f64, acosh64(123123.234375), 12.414088, epsilon));
+    expect(math.approxEqAbs(f64, acosh64(1.5), 0.962424, epsilon));
+    expect(math.approxEqAbs(f64, acosh64(37.45), 4.315976, epsilon));
+    expect(math.approxEqAbs(f64, acosh64(89.123), 5.183133, epsilon));
+    expect(math.approxEqAbs(f64, acosh64(123123.234375), 12.414088, epsilon));
 }
 
 test "math.acosh32.special" {
lib/std/math/asin.zig
@@ -154,23 +154,23 @@ test "math.asin" {
 test "math.asin32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, asin32(0.0), 0.0, epsilon));
-    expect(math.approxEq(f32, asin32(0.2), 0.201358, epsilon));
-    expect(math.approxEq(f32, asin32(-0.2), -0.201358, epsilon));
-    expect(math.approxEq(f32, asin32(0.3434), 0.350535, epsilon));
-    expect(math.approxEq(f32, asin32(0.5), 0.523599, epsilon));
-    expect(math.approxEq(f32, asin32(0.8923), 1.102415, epsilon));
+    expect(math.approxEqAbs(f32, asin32(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f32, asin32(0.2), 0.201358, epsilon));
+    expect(math.approxEqAbs(f32, asin32(-0.2), -0.201358, epsilon));
+    expect(math.approxEqAbs(f32, asin32(0.3434), 0.350535, epsilon));
+    expect(math.approxEqAbs(f32, asin32(0.5), 0.523599, epsilon));
+    expect(math.approxEqAbs(f32, asin32(0.8923), 1.102415, epsilon));
 }
 
 test "math.asin64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, asin64(0.0), 0.0, epsilon));
-    expect(math.approxEq(f64, asin64(0.2), 0.201358, epsilon));
-    expect(math.approxEq(f64, asin64(-0.2), -0.201358, epsilon));
-    expect(math.approxEq(f64, asin64(0.3434), 0.350535, epsilon));
-    expect(math.approxEq(f64, asin64(0.5), 0.523599, epsilon));
-    expect(math.approxEq(f64, asin64(0.8923), 1.102415, epsilon));
+    expect(math.approxEqAbs(f64, asin64(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f64, asin64(0.2), 0.201358, epsilon));
+    expect(math.approxEqAbs(f64, asin64(-0.2), -0.201358, epsilon));
+    expect(math.approxEqAbs(f64, asin64(0.3434), 0.350535, epsilon));
+    expect(math.approxEqAbs(f64, asin64(0.5), 0.523599, epsilon));
+    expect(math.approxEqAbs(f64, asin64(0.8923), 1.102415, epsilon));
 }
 
 test "math.asin32.special" {
lib/std/math/asinh.zig
@@ -101,25 +101,25 @@ test "math.asinh" {
 test "math.asinh32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, asinh32(0.0), 0.0, epsilon));
-    expect(math.approxEq(f32, asinh32(0.2), 0.198690, epsilon));
-    expect(math.approxEq(f32, asinh32(0.8923), 0.803133, epsilon));
-    expect(math.approxEq(f32, asinh32(1.5), 1.194763, epsilon));
-    expect(math.approxEq(f32, asinh32(37.45), 4.316332, epsilon));
-    expect(math.approxEq(f32, asinh32(89.123), 5.183196, epsilon));
-    expect(math.approxEq(f32, asinh32(123123.234375), 12.414088, epsilon));
+    expect(math.approxEqAbs(f32, asinh32(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f32, asinh32(0.2), 0.198690, epsilon));
+    expect(math.approxEqAbs(f32, asinh32(0.8923), 0.803133, epsilon));
+    expect(math.approxEqAbs(f32, asinh32(1.5), 1.194763, epsilon));
+    expect(math.approxEqAbs(f32, asinh32(37.45), 4.316332, epsilon));
+    expect(math.approxEqAbs(f32, asinh32(89.123), 5.183196, epsilon));
+    expect(math.approxEqAbs(f32, asinh32(123123.234375), 12.414088, epsilon));
 }
 
 test "math.asinh64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, asinh64(0.0), 0.0, epsilon));
-    expect(math.approxEq(f64, asinh64(0.2), 0.198690, epsilon));
-    expect(math.approxEq(f64, asinh64(0.8923), 0.803133, epsilon));
-    expect(math.approxEq(f64, asinh64(1.5), 1.194763, epsilon));
-    expect(math.approxEq(f64, asinh64(37.45), 4.316332, epsilon));
-    expect(math.approxEq(f64, asinh64(89.123), 5.183196, epsilon));
-    expect(math.approxEq(f64, asinh64(123123.234375), 12.414088, epsilon));
+    expect(math.approxEqAbs(f64, asinh64(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f64, asinh64(0.2), 0.198690, epsilon));
+    expect(math.approxEqAbs(f64, asinh64(0.8923), 0.803133, epsilon));
+    expect(math.approxEqAbs(f64, asinh64(1.5), 1.194763, epsilon));
+    expect(math.approxEqAbs(f64, asinh64(37.45), 4.316332, epsilon));
+    expect(math.approxEqAbs(f64, asinh64(89.123), 5.183196, epsilon));
+    expect(math.approxEqAbs(f64, asinh64(123123.234375), 12.414088, epsilon));
 }
 
 test "math.asinh32.special" {
lib/std/math/atan.zig
@@ -224,21 +224,21 @@ test "math.atan" {
 test "math.atan32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, atan32(0.2), 0.197396, epsilon));
-    expect(math.approxEq(f32, atan32(-0.2), -0.197396, epsilon));
-    expect(math.approxEq(f32, atan32(0.3434), 0.330783, epsilon));
-    expect(math.approxEq(f32, atan32(0.8923), 0.728545, epsilon));
-    expect(math.approxEq(f32, atan32(1.5), 0.982794, epsilon));
+    expect(math.approxEqAbs(f32, atan32(0.2), 0.197396, epsilon));
+    expect(math.approxEqAbs(f32, atan32(-0.2), -0.197396, epsilon));
+    expect(math.approxEqAbs(f32, atan32(0.3434), 0.330783, epsilon));
+    expect(math.approxEqAbs(f32, atan32(0.8923), 0.728545, epsilon));
+    expect(math.approxEqAbs(f32, atan32(1.5), 0.982794, epsilon));
 }
 
 test "math.atan64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, atan64(0.2), 0.197396, epsilon));
-    expect(math.approxEq(f64, atan64(-0.2), -0.197396, epsilon));
-    expect(math.approxEq(f64, atan64(0.3434), 0.330783, epsilon));
-    expect(math.approxEq(f64, atan64(0.8923), 0.728545, epsilon));
-    expect(math.approxEq(f64, atan64(1.5), 0.982794, epsilon));
+    expect(math.approxEqAbs(f64, atan64(0.2), 0.197396, epsilon));
+    expect(math.approxEqAbs(f64, atan64(-0.2), -0.197396, epsilon));
+    expect(math.approxEqAbs(f64, atan64(0.3434), 0.330783, epsilon));
+    expect(math.approxEqAbs(f64, atan64(0.8923), 0.728545, epsilon));
+    expect(math.approxEqAbs(f64, atan64(1.5), 0.982794, epsilon));
 }
 
 test "math.atan32.special" {
@@ -246,8 +246,8 @@ test "math.atan32.special" {
 
     expect(atan32(0.0) == 0.0);
     expect(atan32(-0.0) == -0.0);
-    expect(math.approxEq(f32, atan32(math.inf(f32)), math.pi / 2.0, epsilon));
-    expect(math.approxEq(f32, atan32(-math.inf(f32)), -math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f32, atan32(math.inf(f32)), math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f32, atan32(-math.inf(f32)), -math.pi / 2.0, epsilon));
 }
 
 test "math.atan64.special" {
@@ -255,6 +255,6 @@ test "math.atan64.special" {
 
     expect(atan64(0.0) == 0.0);
     expect(atan64(-0.0) == -0.0);
-    expect(math.approxEq(f64, atan64(math.inf(f64)), math.pi / 2.0, epsilon));
-    expect(math.approxEq(f64, atan64(-math.inf(f64)), -math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f64, atan64(math.inf(f64)), math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f64, atan64(-math.inf(f64)), -math.pi / 2.0, epsilon));
 }
lib/std/math/atan2.zig
@@ -224,25 +224,25 @@ test "math.atan2" {
 test "math.atan2_32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, atan2_32(0.0, 0.0), 0.0, epsilon));
-    expect(math.approxEq(f32, atan2_32(0.2, 0.2), 0.785398, epsilon));
-    expect(math.approxEq(f32, atan2_32(-0.2, 0.2), -0.785398, epsilon));
-    expect(math.approxEq(f32, atan2_32(0.2, -0.2), 2.356194, epsilon));
-    expect(math.approxEq(f32, atan2_32(-0.2, -0.2), -2.356194, epsilon));
-    expect(math.approxEq(f32, atan2_32(0.34, -0.4), 2.437099, epsilon));
-    expect(math.approxEq(f32, atan2_32(0.34, 1.243), 0.267001, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(0.0, 0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(0.2, 0.2), 0.785398, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(-0.2, 0.2), -0.785398, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(0.2, -0.2), 2.356194, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(-0.2, -0.2), -2.356194, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(0.34, -0.4), 2.437099, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(0.34, 1.243), 0.267001, epsilon));
 }
 
 test "math.atan2_64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, atan2_64(0.0, 0.0), 0.0, epsilon));
-    expect(math.approxEq(f64, atan2_64(0.2, 0.2), 0.785398, epsilon));
-    expect(math.approxEq(f64, atan2_64(-0.2, 0.2), -0.785398, epsilon));
-    expect(math.approxEq(f64, atan2_64(0.2, -0.2), 2.356194, epsilon));
-    expect(math.approxEq(f64, atan2_64(-0.2, -0.2), -2.356194, epsilon));
-    expect(math.approxEq(f64, atan2_64(0.34, -0.4), 2.437099, epsilon));
-    expect(math.approxEq(f64, atan2_64(0.34, 1.243), 0.267001, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(0.0, 0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(0.2, 0.2), 0.785398, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(-0.2, 0.2), -0.785398, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(0.2, -0.2), 2.356194, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(-0.2, -0.2), -2.356194, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(0.34, -0.4), 2.437099, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(0.34, 1.243), 0.267001, epsilon));
 }
 
 test "math.atan2_32.special" {
@@ -252,21 +252,21 @@ test "math.atan2_32.special" {
     expect(math.isNan(atan2_32(math.nan(f32), 1.0)));
     expect(atan2_32(0.0, 5.0) == 0.0);
     expect(atan2_32(-0.0, 5.0) == -0.0);
-    expect(math.approxEq(f32, atan2_32(0.0, -5.0), math.pi, epsilon));
-    //expect(math.approxEq(f32, atan2_32(-0.0, -5.0), -math.pi, epsilon)); TODO support negative zero?
-    expect(math.approxEq(f32, atan2_32(1.0, 0.0), math.pi / 2.0, epsilon));
-    expect(math.approxEq(f32, atan2_32(1.0, -0.0), math.pi / 2.0, epsilon));
-    expect(math.approxEq(f32, atan2_32(-1.0, 0.0), -math.pi / 2.0, epsilon));
-    expect(math.approxEq(f32, atan2_32(-1.0, -0.0), -math.pi / 2.0, epsilon));
-    expect(math.approxEq(f32, atan2_32(math.inf(f32), math.inf(f32)), math.pi / 4.0, epsilon));
-    expect(math.approxEq(f32, atan2_32(-math.inf(f32), math.inf(f32)), -math.pi / 4.0, epsilon));
-    expect(math.approxEq(f32, atan2_32(math.inf(f32), -math.inf(f32)), 3.0 * math.pi / 4.0, epsilon));
-    expect(math.approxEq(f32, atan2_32(-math.inf(f32), -math.inf(f32)), -3.0 * math.pi / 4.0, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(0.0, -5.0), math.pi, epsilon));
+    //expect(math.approxEqAbs(f32, atan2_32(-0.0, -5.0), -math.pi, .{.rel=0,.abs=epsilon})); TODO support negative zero?
+    expect(math.approxEqAbs(f32, atan2_32(1.0, 0.0), math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(1.0, -0.0), math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(-1.0, 0.0), -math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(-1.0, -0.0), -math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(math.inf(f32), math.inf(f32)), math.pi / 4.0, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(-math.inf(f32), math.inf(f32)), -math.pi / 4.0, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(math.inf(f32), -math.inf(f32)), 3.0 * math.pi / 4.0, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(-math.inf(f32), -math.inf(f32)), -3.0 * math.pi / 4.0, epsilon));
     expect(atan2_32(1.0, math.inf(f32)) == 0.0);
-    expect(math.approxEq(f32, atan2_32(1.0, -math.inf(f32)), math.pi, epsilon));
-    expect(math.approxEq(f32, atan2_32(-1.0, -math.inf(f32)), -math.pi, epsilon));
-    expect(math.approxEq(f32, atan2_32(math.inf(f32), 1.0), math.pi / 2.0, epsilon));
-    expect(math.approxEq(f32, atan2_32(-math.inf(f32), 1.0), -math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(1.0, -math.inf(f32)), math.pi, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(-1.0, -math.inf(f32)), -math.pi, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(math.inf(f32), 1.0), math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f32, atan2_32(-math.inf(f32), 1.0), -math.pi / 2.0, epsilon));
 }
 
 test "math.atan2_64.special" {
@@ -276,19 +276,19 @@ test "math.atan2_64.special" {
     expect(math.isNan(atan2_64(math.nan(f64), 1.0)));
     expect(atan2_64(0.0, 5.0) == 0.0);
     expect(atan2_64(-0.0, 5.0) == -0.0);
-    expect(math.approxEq(f64, atan2_64(0.0, -5.0), math.pi, epsilon));
-    //expect(math.approxEq(f64, atan2_64(-0.0, -5.0), -math.pi, epsilon)); TODO support negative zero?
-    expect(math.approxEq(f64, atan2_64(1.0, 0.0), math.pi / 2.0, epsilon));
-    expect(math.approxEq(f64, atan2_64(1.0, -0.0), math.pi / 2.0, epsilon));
-    expect(math.approxEq(f64, atan2_64(-1.0, 0.0), -math.pi / 2.0, epsilon));
-    expect(math.approxEq(f64, atan2_64(-1.0, -0.0), -math.pi / 2.0, epsilon));
-    expect(math.approxEq(f64, atan2_64(math.inf(f64), math.inf(f64)), math.pi / 4.0, epsilon));
-    expect(math.approxEq(f64, atan2_64(-math.inf(f64), math.inf(f64)), -math.pi / 4.0, epsilon));
-    expect(math.approxEq(f64, atan2_64(math.inf(f64), -math.inf(f64)), 3.0 * math.pi / 4.0, epsilon));
-    expect(math.approxEq(f64, atan2_64(-math.inf(f64), -math.inf(f64)), -3.0 * math.pi / 4.0, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(0.0, -5.0), math.pi, epsilon));
+    //expect(math.approxEqAbs(f64, atan2_64(-0.0, -5.0), -math.pi, .{.rel=0,.abs=epsilon})); TODO support negative zero?
+    expect(math.approxEqAbs(f64, atan2_64(1.0, 0.0), math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(1.0, -0.0), math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(-1.0, 0.0), -math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(-1.0, -0.0), -math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(math.inf(f64), math.inf(f64)), math.pi / 4.0, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(-math.inf(f64), math.inf(f64)), -math.pi / 4.0, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(math.inf(f64), -math.inf(f64)), 3.0 * math.pi / 4.0, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(-math.inf(f64), -math.inf(f64)), -3.0 * math.pi / 4.0, epsilon));
     expect(atan2_64(1.0, math.inf(f64)) == 0.0);
-    expect(math.approxEq(f64, atan2_64(1.0, -math.inf(f64)), math.pi, epsilon));
-    expect(math.approxEq(f64, atan2_64(-1.0, -math.inf(f64)), -math.pi, epsilon));
-    expect(math.approxEq(f64, atan2_64(math.inf(f64), 1.0), math.pi / 2.0, epsilon));
-    expect(math.approxEq(f64, atan2_64(-math.inf(f64), 1.0), -math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(1.0, -math.inf(f64)), math.pi, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(-1.0, -math.inf(f64)), -math.pi, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(math.inf(f64), 1.0), math.pi / 2.0, epsilon));
+    expect(math.approxEqAbs(f64, atan2_64(-math.inf(f64), 1.0), -math.pi / 2.0, epsilon));
 }
lib/std/math/atanh.zig
@@ -96,17 +96,17 @@ test "math.atanh" {
 test "math.atanh_32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, atanh_32(0.0), 0.0, epsilon));
-    expect(math.approxEq(f32, atanh_32(0.2), 0.202733, epsilon));
-    expect(math.approxEq(f32, atanh_32(0.8923), 1.433099, epsilon));
+    expect(math.approxEqAbs(f32, atanh_32(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f32, atanh_32(0.2), 0.202733, epsilon));
+    expect(math.approxEqAbs(f32, atanh_32(0.8923), 1.433099, epsilon));
 }
 
 test "math.atanh_64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, atanh_64(0.0), 0.0, epsilon));
-    expect(math.approxEq(f64, atanh_64(0.2), 0.202733, epsilon));
-    expect(math.approxEq(f64, atanh_64(0.8923), 1.433099, epsilon));
+    expect(math.approxEqAbs(f64, atanh_64(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f64, atanh_64(0.2), 0.202733, epsilon));
+    expect(math.approxEqAbs(f64, atanh_64(0.8923), 1.433099, epsilon));
 }
 
 test "math.atanh32.special" {
lib/std/math/cbrt.zig
@@ -133,22 +133,22 @@ test "math.cbrt32" {
     const epsilon = 0.000001;
 
     expect(cbrt32(0.0) == 0.0);
-    expect(math.approxEq(f32, cbrt32(0.2), 0.584804, epsilon));
-    expect(math.approxEq(f32, cbrt32(0.8923), 0.962728, epsilon));
-    expect(math.approxEq(f32, cbrt32(1.5), 1.144714, epsilon));
-    expect(math.approxEq(f32, cbrt32(37.45), 3.345676, epsilon));
-    expect(math.approxEq(f32, cbrt32(123123.234375), 49.748501, epsilon));
+    expect(math.approxEqAbs(f32, cbrt32(0.2), 0.584804, epsilon));
+    expect(math.approxEqAbs(f32, cbrt32(0.8923), 0.962728, epsilon));
+    expect(math.approxEqAbs(f32, cbrt32(1.5), 1.144714, epsilon));
+    expect(math.approxEqAbs(f32, cbrt32(37.45), 3.345676, epsilon));
+    expect(math.approxEqAbs(f32, cbrt32(123123.234375), 49.748501, epsilon));
 }
 
 test "math.cbrt64" {
     const epsilon = 0.000001;
 
     expect(cbrt64(0.0) == 0.0);
-    expect(math.approxEq(f64, cbrt64(0.2), 0.584804, epsilon));
-    expect(math.approxEq(f64, cbrt64(0.8923), 0.962728, epsilon));
-    expect(math.approxEq(f64, cbrt64(1.5), 1.144714, epsilon));
-    expect(math.approxEq(f64, cbrt64(37.45), 3.345676, epsilon));
-    expect(math.approxEq(f64, cbrt64(123123.234375), 49.748501, epsilon));
+    expect(math.approxEqAbs(f64, cbrt64(0.2), 0.584804, epsilon));
+    expect(math.approxEqAbs(f64, cbrt64(0.8923), 0.962728, epsilon));
+    expect(math.approxEqAbs(f64, cbrt64(1.5), 1.144714, epsilon));
+    expect(math.approxEqAbs(f64, cbrt64(37.45), 3.345676, epsilon));
+    expect(math.approxEqAbs(f64, cbrt64(123123.234375), 49.748501, epsilon));
 }
 
 test "math.cbrt.special" {
lib/std/math/complex.zig
@@ -138,8 +138,8 @@ test "complex.div" {
     const b = Complex(f32).new(2, 7);
     const c = a.div(b);
 
-    testing.expect(math.approxEq(f32, c.re, @as(f32, 31) / 53, epsilon) and
-        math.approxEq(f32, c.im, @as(f32, -29) / 53, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, @as(f32, 31) / 53, epsilon) and
+        math.approxEqAbs(f32, c.im, @as(f32, -29) / 53, epsilon));
 }
 
 test "complex.conjugate" {
@@ -153,15 +153,15 @@ test "complex.reciprocal" {
     const a = Complex(f32).new(5, 3);
     const c = a.reciprocal();
 
-    testing.expect(math.approxEq(f32, c.re, @as(f32, 5) / 34, epsilon) and
-        math.approxEq(f32, c.im, @as(f32, -3) / 34, epsilon));
+    testing.expect(math.approxEqAbs(f32, c.re, @as(f32, 5) / 34, epsilon) and
+        math.approxEqAbs(f32, c.im, @as(f32, -3) / 34, epsilon));
 }
 
 test "complex.magnitude" {
     const a = Complex(f32).new(5, 3);
     const c = a.magnitude();
 
-    testing.expect(math.approxEq(f32, c, 5.83095, epsilon));
+    testing.expect(math.approxEqAbs(f32, c, 5.83095, epsilon));
 }
 
 test "complex.cmath" {
lib/std/math/cos.zig
@@ -95,25 +95,25 @@ test "math.cos" {
 test "math.cos32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, cos_(f32, 0.0), 1.0, epsilon));
-    expect(math.approxEq(f32, cos_(f32, 0.2), 0.980067, epsilon));
-    expect(math.approxEq(f32, cos_(f32, 0.8923), 0.627623, epsilon));
-    expect(math.approxEq(f32, cos_(f32, 1.5), 0.070737, epsilon));
-    expect(math.approxEq(f32, cos_(f32, -1.5), 0.070737, epsilon));
-    expect(math.approxEq(f32, cos_(f32, 37.45), 0.969132, epsilon));
-    expect(math.approxEq(f32, cos_(f32, 89.123), 0.400798, epsilon));
+    expect(math.approxEqAbs(f32, cos_(f32, 0.0), 1.0, epsilon));
+    expect(math.approxEqAbs(f32, cos_(f32, 0.2), 0.980067, epsilon));
+    expect(math.approxEqAbs(f32, cos_(f32, 0.8923), 0.627623, epsilon));
+    expect(math.approxEqAbs(f32, cos_(f32, 1.5), 0.070737, epsilon));
+    expect(math.approxEqAbs(f32, cos_(f32, -1.5), 0.070737, epsilon));
+    expect(math.approxEqAbs(f32, cos_(f32, 37.45), 0.969132, epsilon));
+    expect(math.approxEqAbs(f32, cos_(f32, 89.123), 0.400798, epsilon));
 }
 
 test "math.cos64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, cos_(f64, 0.0), 1.0, epsilon));
-    expect(math.approxEq(f64, cos_(f64, 0.2), 0.980067, epsilon));
-    expect(math.approxEq(f64, cos_(f64, 0.8923), 0.627623, epsilon));
-    expect(math.approxEq(f64, cos_(f64, 1.5), 0.070737, epsilon));
-    expect(math.approxEq(f64, cos_(f64, -1.5), 0.070737, epsilon));
-    expect(math.approxEq(f64, cos_(f64, 37.45), 0.969132, epsilon));
-    expect(math.approxEq(f64, cos_(f64, 89.123), 0.40080, epsilon));
+    expect(math.approxEqAbs(f64, cos_(f64, 0.0), 1.0, epsilon));
+    expect(math.approxEqAbs(f64, cos_(f64, 0.2), 0.980067, epsilon));
+    expect(math.approxEqAbs(f64, cos_(f64, 0.8923), 0.627623, epsilon));
+    expect(math.approxEqAbs(f64, cos_(f64, 1.5), 0.070737, epsilon));
+    expect(math.approxEqAbs(f64, cos_(f64, -1.5), 0.070737, epsilon));
+    expect(math.approxEqAbs(f64, cos_(f64, 37.45), 0.969132, epsilon));
+    expect(math.approxEqAbs(f64, cos_(f64, 89.123), 0.40080, epsilon));
 }
 
 test "math.cos32.special" {
lib/std/math/cosh.zig
@@ -100,27 +100,27 @@ test "math.cosh" {
 test "math.cosh32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, cosh32(0.0), 1.0, epsilon));
-    expect(math.approxEq(f32, cosh32(0.2), 1.020067, epsilon));
-    expect(math.approxEq(f32, cosh32(0.8923), 1.425225, epsilon));
-    expect(math.approxEq(f32, cosh32(1.5), 2.352410, epsilon));
-    expect(math.approxEq(f32, cosh32(-0.0), 1.0, epsilon));
-    expect(math.approxEq(f32, cosh32(-0.2), 1.020067, epsilon));
-    expect(math.approxEq(f32, cosh32(-0.8923), 1.425225, epsilon));
-    expect(math.approxEq(f32, cosh32(-1.5), 2.352410, epsilon));
+    expect(math.approxEqAbs(f32, cosh32(0.0), 1.0, epsilon));
+    expect(math.approxEqAbs(f32, cosh32(0.2), 1.020067, epsilon));
+    expect(math.approxEqAbs(f32, cosh32(0.8923), 1.425225, epsilon));
+    expect(math.approxEqAbs(f32, cosh32(1.5), 2.352410, epsilon));
+    expect(math.approxEqAbs(f32, cosh32(-0.0), 1.0, epsilon));
+    expect(math.approxEqAbs(f32, cosh32(-0.2), 1.020067, epsilon));
+    expect(math.approxEqAbs(f32, cosh32(-0.8923), 1.425225, epsilon));
+    expect(math.approxEqAbs(f32, cosh32(-1.5), 2.352410, epsilon));
 }
 
 test "math.cosh64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, cosh64(0.0), 1.0, epsilon));
-    expect(math.approxEq(f64, cosh64(0.2), 1.020067, epsilon));
-    expect(math.approxEq(f64, cosh64(0.8923), 1.425225, epsilon));
-    expect(math.approxEq(f64, cosh64(1.5), 2.352410, epsilon));
-    expect(math.approxEq(f64, cosh64(-0.0), 1.0, epsilon));
-    expect(math.approxEq(f64, cosh64(-0.2), 1.020067, epsilon));
-    expect(math.approxEq(f64, cosh64(-0.8923), 1.425225, epsilon));
-    expect(math.approxEq(f64, cosh64(-1.5), 2.352410, epsilon));
+    expect(math.approxEqAbs(f64, cosh64(0.0), 1.0, epsilon));
+    expect(math.approxEqAbs(f64, cosh64(0.2), 1.020067, epsilon));
+    expect(math.approxEqAbs(f64, cosh64(0.8923), 1.425225, epsilon));
+    expect(math.approxEqAbs(f64, cosh64(1.5), 2.352410, epsilon));
+    expect(math.approxEqAbs(f64, cosh64(-0.0), 1.0, epsilon));
+    expect(math.approxEqAbs(f64, cosh64(-0.2), 1.020067, epsilon));
+    expect(math.approxEqAbs(f64, cosh64(-0.8923), 1.425225, epsilon));
+    expect(math.approxEqAbs(f64, cosh64(-1.5), 2.352410, epsilon));
 }
 
 test "math.cosh32.special" {
lib/std/math/epsilon.zig
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+const math = @import("../math.zig");
+
+/// Returns the machine epsilon for type T.
+/// This is the smallest value of type T that satisfies the inequality 1.0 +
+/// epsilon != 1.0.
+pub fn epsilon(comptime T: type) T {
+    return switch (T) {
+        f16 => math.f16_epsilon,
+        f32 => math.f32_epsilon,
+        f64 => math.f64_epsilon,
+        f128 => math.f128_epsilon,
+        else => @compileError("epsilon not implemented for " ++ @typeName(T)),
+    };
+}
lib/std/math/exp.zig
@@ -196,20 +196,20 @@ test "math.exp32" {
     const epsilon = 0.000001;
 
     assert(exp32(0.0) == 1.0);
-    assert(math.approxEq(f32, exp32(0.0), 1.0, epsilon));
-    assert(math.approxEq(f32, exp32(0.2), 1.221403, epsilon));
-    assert(math.approxEq(f32, exp32(0.8923), 2.440737, epsilon));
-    assert(math.approxEq(f32, exp32(1.5), 4.481689, epsilon));
+    assert(math.approxEqAbs(f32, exp32(0.0), 1.0, epsilon));
+    assert(math.approxEqAbs(f32, exp32(0.2), 1.221403, epsilon));
+    assert(math.approxEqAbs(f32, exp32(0.8923), 2.440737, epsilon));
+    assert(math.approxEqAbs(f32, exp32(1.5), 4.481689, epsilon));
 }
 
 test "math.exp64" {
     const epsilon = 0.000001;
 
     assert(exp64(0.0) == 1.0);
-    assert(math.approxEq(f64, exp64(0.0), 1.0, epsilon));
-    assert(math.approxEq(f64, exp64(0.2), 1.221403, epsilon));
-    assert(math.approxEq(f64, exp64(0.8923), 2.440737, epsilon));
-    assert(math.approxEq(f64, exp64(1.5), 4.481689, epsilon));
+    assert(math.approxEqAbs(f64, exp64(0.0), 1.0, epsilon));
+    assert(math.approxEqAbs(f64, exp64(0.2), 1.221403, epsilon));
+    assert(math.approxEqAbs(f64, exp64(0.8923), 2.440737, epsilon));
+    assert(math.approxEqAbs(f64, exp64(1.5), 4.481689, epsilon));
 }
 
 test "math.exp32.special" {
lib/std/math/exp2.zig
@@ -434,19 +434,19 @@ test "math.exp2_32" {
     const epsilon = 0.000001;
 
     expect(exp2_32(0.0) == 1.0);
-    expect(math.approxEq(f32, exp2_32(0.2), 1.148698, epsilon));
-    expect(math.approxEq(f32, exp2_32(0.8923), 1.856133, epsilon));
-    expect(math.approxEq(f32, exp2_32(1.5), 2.828427, epsilon));
-    expect(math.approxEq(f32, exp2_32(37.45), 187747237888, epsilon));
+    expect(math.approxEqAbs(f32, exp2_32(0.2), 1.148698, epsilon));
+    expect(math.approxEqAbs(f32, exp2_32(0.8923), 1.856133, epsilon));
+    expect(math.approxEqAbs(f32, exp2_32(1.5), 2.828427, epsilon));
+    expect(math.approxEqAbs(f32, exp2_32(37.45), 187747237888, epsilon));
 }
 
 test "math.exp2_64" {
     const epsilon = 0.000001;
 
     expect(exp2_64(0.0) == 1.0);
-    expect(math.approxEq(f64, exp2_64(0.2), 1.148698, epsilon));
-    expect(math.approxEq(f64, exp2_64(0.8923), 1.856133, epsilon));
-    expect(math.approxEq(f64, exp2_64(1.5), 2.828427, epsilon));
+    expect(math.approxEqAbs(f64, exp2_64(0.2), 1.148698, epsilon));
+    expect(math.approxEqAbs(f64, exp2_64(0.8923), 1.856133, epsilon));
+    expect(math.approxEqAbs(f64, exp2_64(1.5), 2.828427, epsilon));
 }
 
 test "math.exp2_32.special" {
lib/std/math/expm1.zig
@@ -300,20 +300,20 @@ test "math.expm1_32" {
     const epsilon = 0.000001;
 
     expect(expm1_32(0.0) == 0.0);
-    expect(math.approxEq(f32, expm1_32(0.0), 0.0, epsilon));
-    expect(math.approxEq(f32, expm1_32(0.2), 0.221403, epsilon));
-    expect(math.approxEq(f32, expm1_32(0.8923), 1.440737, epsilon));
-    expect(math.approxEq(f32, expm1_32(1.5), 3.481689, epsilon));
+    expect(math.approxEqAbs(f32, expm1_32(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f32, expm1_32(0.2), 0.221403, epsilon));
+    expect(math.approxEqAbs(f32, expm1_32(0.8923), 1.440737, epsilon));
+    expect(math.approxEqAbs(f32, expm1_32(1.5), 3.481689, epsilon));
 }
 
 test "math.expm1_64" {
     const epsilon = 0.000001;
 
     expect(expm1_64(0.0) == 0.0);
-    expect(math.approxEq(f64, expm1_64(0.0), 0.0, epsilon));
-    expect(math.approxEq(f64, expm1_64(0.2), 0.221403, epsilon));
-    expect(math.approxEq(f64, expm1_64(0.8923), 1.440737, epsilon));
-    expect(math.approxEq(f64, expm1_64(1.5), 3.481689, epsilon));
+    expect(math.approxEqAbs(f64, expm1_64(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f64, expm1_64(0.2), 0.221403, epsilon));
+    expect(math.approxEqAbs(f64, expm1_64(0.8923), 1.440737, epsilon));
+    expect(math.approxEqAbs(f64, expm1_64(1.5), 3.481689, epsilon));
 }
 
 test "math.expm1_32.special" {
lib/std/math/fma.zig
@@ -155,23 +155,23 @@ test "math.fma" {
 test "math.fma32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, fma32(0.0, 5.0, 9.124), 9.124, epsilon));
-    expect(math.approxEq(f32, fma32(0.2, 5.0, 9.124), 10.124, epsilon));
-    expect(math.approxEq(f32, fma32(0.8923, 5.0, 9.124), 13.5855, epsilon));
-    expect(math.approxEq(f32, fma32(1.5, 5.0, 9.124), 16.624, epsilon));
-    expect(math.approxEq(f32, fma32(37.45, 5.0, 9.124), 196.374004, epsilon));
-    expect(math.approxEq(f32, fma32(89.123, 5.0, 9.124), 454.739005, epsilon));
-    expect(math.approxEq(f32, fma32(123123.234375, 5.0, 9.124), 615625.295875, epsilon));
+    expect(math.approxEqAbs(f32, fma32(0.0, 5.0, 9.124), 9.124, epsilon));
+    expect(math.approxEqAbs(f32, fma32(0.2, 5.0, 9.124), 10.124, epsilon));
+    expect(math.approxEqAbs(f32, fma32(0.8923, 5.0, 9.124), 13.5855, epsilon));
+    expect(math.approxEqAbs(f32, fma32(1.5, 5.0, 9.124), 16.624, epsilon));
+    expect(math.approxEqAbs(f32, fma32(37.45, 5.0, 9.124), 196.374004, epsilon));
+    expect(math.approxEqAbs(f32, fma32(89.123, 5.0, 9.124), 454.739005, epsilon));
+    expect(math.approxEqAbs(f32, fma32(123123.234375, 5.0, 9.124), 615625.295875, epsilon));
 }
 
 test "math.fma64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, fma64(0.0, 5.0, 9.124), 9.124, epsilon));
-    expect(math.approxEq(f64, fma64(0.2, 5.0, 9.124), 10.124, epsilon));
-    expect(math.approxEq(f64, fma64(0.8923, 5.0, 9.124), 13.5855, epsilon));
-    expect(math.approxEq(f64, fma64(1.5, 5.0, 9.124), 16.624, epsilon));
-    expect(math.approxEq(f64, fma64(37.45, 5.0, 9.124), 196.374, epsilon));
-    expect(math.approxEq(f64, fma64(89.123, 5.0, 9.124), 454.739, epsilon));
-    expect(math.approxEq(f64, fma64(123123.234375, 5.0, 9.124), 615625.295875, epsilon));
+    expect(math.approxEqAbs(f64, fma64(0.0, 5.0, 9.124), 9.124, epsilon));
+    expect(math.approxEqAbs(f64, fma64(0.2, 5.0, 9.124), 10.124, epsilon));
+    expect(math.approxEqAbs(f64, fma64(0.8923, 5.0, 9.124), 13.5855, epsilon));
+    expect(math.approxEqAbs(f64, fma64(1.5, 5.0, 9.124), 16.624, epsilon));
+    expect(math.approxEqAbs(f64, fma64(37.45, 5.0, 9.124), 196.374, epsilon));
+    expect(math.approxEqAbs(f64, fma64(89.123, 5.0, 9.124), 454.739, epsilon));
+    expect(math.approxEqAbs(f64, fma64(123123.234375, 5.0, 9.124), 615625.295875, epsilon));
 }
lib/std/math/frexp.zig
@@ -127,10 +127,10 @@ test "math.frexp32" {
     var r: frexp32_result = undefined;
 
     r = frexp32(1.3);
-    expect(math.approxEq(f32, r.significand, 0.65, epsilon) and r.exponent == 1);
+    expect(math.approxEqAbs(f32, r.significand, 0.65, epsilon) and r.exponent == 1);
 
     r = frexp32(78.0234);
-    expect(math.approxEq(f32, r.significand, 0.609558, epsilon) and r.exponent == 7);
+    expect(math.approxEqAbs(f32, r.significand, 0.609558, epsilon) and r.exponent == 7);
 }
 
 test "math.frexp64" {
@@ -138,10 +138,10 @@ test "math.frexp64" {
     var r: frexp64_result = undefined;
 
     r = frexp64(1.3);
-    expect(math.approxEq(f64, r.significand, 0.65, epsilon) and r.exponent == 1);
+    expect(math.approxEqAbs(f64, r.significand, 0.65, epsilon) and r.exponent == 1);
 
     r = frexp64(78.0234);
-    expect(math.approxEq(f64, r.significand, 0.609558, epsilon) and r.exponent == 7);
+    expect(math.approxEqAbs(f64, r.significand, 0.609558, epsilon) and r.exponent == 7);
 }
 
 test "math.frexp32.special" {
lib/std/math/hypot.zig
@@ -133,25 +133,25 @@ test "math.hypot" {
 test "math.hypot32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, hypot32(0.0, -1.2), 1.2, epsilon));
-    expect(math.approxEq(f32, hypot32(0.2, -0.34), 0.394462, epsilon));
-    expect(math.approxEq(f32, hypot32(0.8923, 2.636890), 2.783772, epsilon));
-    expect(math.approxEq(f32, hypot32(1.5, 5.25), 5.460083, epsilon));
-    expect(math.approxEq(f32, hypot32(37.45, 159.835), 164.163742, epsilon));
-    expect(math.approxEq(f32, hypot32(89.123, 382.028905), 392.286865, epsilon));
-    expect(math.approxEq(f32, hypot32(123123.234375, 529428.707813), 543556.875, epsilon));
+    expect(math.approxEqAbs(f32, hypot32(0.0, -1.2), 1.2, epsilon));
+    expect(math.approxEqAbs(f32, hypot32(0.2, -0.34), 0.394462, epsilon));
+    expect(math.approxEqAbs(f32, hypot32(0.8923, 2.636890), 2.783772, epsilon));
+    expect(math.approxEqAbs(f32, hypot32(1.5, 5.25), 5.460083, epsilon));
+    expect(math.approxEqAbs(f32, hypot32(37.45, 159.835), 164.163742, epsilon));
+    expect(math.approxEqAbs(f32, hypot32(89.123, 382.028905), 392.286865, epsilon));
+    expect(math.approxEqAbs(f32, hypot32(123123.234375, 529428.707813), 543556.875, epsilon));
 }
 
 test "math.hypot64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, hypot64(0.0, -1.2), 1.2, epsilon));
-    expect(math.approxEq(f64, hypot64(0.2, -0.34), 0.394462, epsilon));
-    expect(math.approxEq(f64, hypot64(0.8923, 2.636890), 2.783772, epsilon));
-    expect(math.approxEq(f64, hypot64(1.5, 5.25), 5.460082, epsilon));
-    expect(math.approxEq(f64, hypot64(37.45, 159.835), 164.163728, epsilon));
-    expect(math.approxEq(f64, hypot64(89.123, 382.028905), 392.286876, epsilon));
-    expect(math.approxEq(f64, hypot64(123123.234375, 529428.707813), 543556.885247, epsilon));
+    expect(math.approxEqAbs(f64, hypot64(0.0, -1.2), 1.2, epsilon));
+    expect(math.approxEqAbs(f64, hypot64(0.2, -0.34), 0.394462, epsilon));
+    expect(math.approxEqAbs(f64, hypot64(0.8923, 2.636890), 2.783772, epsilon));
+    expect(math.approxEqAbs(f64, hypot64(1.5, 5.25), 5.460082, epsilon));
+    expect(math.approxEqAbs(f64, hypot64(37.45, 159.835), 164.163728, epsilon));
+    expect(math.approxEqAbs(f64, hypot64(89.123, 382.028905), 392.286876, epsilon));
+    expect(math.approxEqAbs(f64, hypot64(123123.234375, 529428.707813), 543556.885247, epsilon));
 }
 
 test "math.hypot32.special" {
lib/std/math/ln.zig
@@ -159,23 +159,23 @@ test "math.ln" {
 test "math.ln32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, ln_32(0.2), -1.609438, epsilon));
-    expect(math.approxEq(f32, ln_32(0.8923), -0.113953, epsilon));
-    expect(math.approxEq(f32, ln_32(1.5), 0.405465, epsilon));
-    expect(math.approxEq(f32, ln_32(37.45), 3.623007, epsilon));
-    expect(math.approxEq(f32, ln_32(89.123), 4.490017, epsilon));
-    expect(math.approxEq(f32, ln_32(123123.234375), 11.720941, epsilon));
+    expect(math.approxEqAbs(f32, ln_32(0.2), -1.609438, epsilon));
+    expect(math.approxEqAbs(f32, ln_32(0.8923), -0.113953, epsilon));
+    expect(math.approxEqAbs(f32, ln_32(1.5), 0.405465, epsilon));
+    expect(math.approxEqAbs(f32, ln_32(37.45), 3.623007, epsilon));
+    expect(math.approxEqAbs(f32, ln_32(89.123), 4.490017, epsilon));
+    expect(math.approxEqAbs(f32, ln_32(123123.234375), 11.720941, epsilon));
 }
 
 test "math.ln64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, ln_64(0.2), -1.609438, epsilon));
-    expect(math.approxEq(f64, ln_64(0.8923), -0.113953, epsilon));
-    expect(math.approxEq(f64, ln_64(1.5), 0.405465, epsilon));
-    expect(math.approxEq(f64, ln_64(37.45), 3.623007, epsilon));
-    expect(math.approxEq(f64, ln_64(89.123), 4.490017, epsilon));
-    expect(math.approxEq(f64, ln_64(123123.234375), 11.720941, epsilon));
+    expect(math.approxEqAbs(f64, ln_64(0.2), -1.609438, epsilon));
+    expect(math.approxEqAbs(f64, ln_64(0.8923), -0.113953, epsilon));
+    expect(math.approxEqAbs(f64, ln_64(1.5), 0.405465, epsilon));
+    expect(math.approxEqAbs(f64, ln_64(37.45), 3.623007, epsilon));
+    expect(math.approxEqAbs(f64, ln_64(89.123), 4.490017, epsilon));
+    expect(math.approxEqAbs(f64, ln_64(123123.234375), 11.720941, epsilon));
 }
 
 test "math.ln32.special" {
lib/std/math/log.zig
@@ -61,9 +61,9 @@ test "math.log integer" {
 test "math.log float" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, log(f32, 6, 0.23947), -0.797723, epsilon));
-    expect(math.approxEq(f32, log(f32, 89, 0.23947), -0.318432, epsilon));
-    expect(math.approxEq(f64, log(f64, 123897, 12389216414), 1.981724596, epsilon));
+    expect(math.approxEqAbs(f32, log(f32, 6, 0.23947), -0.797723, epsilon));
+    expect(math.approxEqAbs(f32, log(f32, 89, 0.23947), -0.318432, epsilon));
+    expect(math.approxEqAbs(f64, log(f64, 123897, 12389216414), 1.981724596, epsilon));
 }
 
 test "math.log float_special" {
lib/std/math/log10.zig
@@ -187,23 +187,23 @@ test "math.log10" {
 test "math.log10_32" {
     const epsilon = 0.000001;
 
-    testing.expect(math.approxEq(f32, log10_32(0.2), -0.698970, epsilon));
-    testing.expect(math.approxEq(f32, log10_32(0.8923), -0.049489, epsilon));
-    testing.expect(math.approxEq(f32, log10_32(1.5), 0.176091, epsilon));
-    testing.expect(math.approxEq(f32, log10_32(37.45), 1.573452, epsilon));
-    testing.expect(math.approxEq(f32, log10_32(89.123), 1.94999, epsilon));
-    testing.expect(math.approxEq(f32, log10_32(123123.234375), 5.09034, epsilon));
+    testing.expect(math.approxEqAbs(f32, log10_32(0.2), -0.698970, epsilon));
+    testing.expect(math.approxEqAbs(f32, log10_32(0.8923), -0.049489, epsilon));
+    testing.expect(math.approxEqAbs(f32, log10_32(1.5), 0.176091, epsilon));
+    testing.expect(math.approxEqAbs(f32, log10_32(37.45), 1.573452, epsilon));
+    testing.expect(math.approxEqAbs(f32, log10_32(89.123), 1.94999, epsilon));
+    testing.expect(math.approxEqAbs(f32, log10_32(123123.234375), 5.09034, epsilon));
 }
 
 test "math.log10_64" {
     const epsilon = 0.000001;
 
-    testing.expect(math.approxEq(f64, log10_64(0.2), -0.698970, epsilon));
-    testing.expect(math.approxEq(f64, log10_64(0.8923), -0.049489, epsilon));
-    testing.expect(math.approxEq(f64, log10_64(1.5), 0.176091, epsilon));
-    testing.expect(math.approxEq(f64, log10_64(37.45), 1.573452, epsilon));
-    testing.expect(math.approxEq(f64, log10_64(89.123), 1.94999, epsilon));
-    testing.expect(math.approxEq(f64, log10_64(123123.234375), 5.09034, epsilon));
+    testing.expect(math.approxEqAbs(f64, log10_64(0.2), -0.698970, epsilon));
+    testing.expect(math.approxEqAbs(f64, log10_64(0.8923), -0.049489, epsilon));
+    testing.expect(math.approxEqAbs(f64, log10_64(1.5), 0.176091, epsilon));
+    testing.expect(math.approxEqAbs(f64, log10_64(37.45), 1.573452, epsilon));
+    testing.expect(math.approxEqAbs(f64, log10_64(89.123), 1.94999, epsilon));
+    testing.expect(math.approxEqAbs(f64, log10_64(123123.234375), 5.09034, epsilon));
 }
 
 test "math.log10_32.special" {
lib/std/math/log1p.zig
@@ -195,25 +195,25 @@ test "math.log1p" {
 test "math.log1p_32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, log1p_32(0.0), 0.0, epsilon));
-    expect(math.approxEq(f32, log1p_32(0.2), 0.182322, epsilon));
-    expect(math.approxEq(f32, log1p_32(0.8923), 0.637793, epsilon));
-    expect(math.approxEq(f32, log1p_32(1.5), 0.916291, epsilon));
-    expect(math.approxEq(f32, log1p_32(37.45), 3.649359, epsilon));
-    expect(math.approxEq(f32, log1p_32(89.123), 4.501175, epsilon));
-    expect(math.approxEq(f32, log1p_32(123123.234375), 11.720949, epsilon));
+    expect(math.approxEqAbs(f32, log1p_32(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f32, log1p_32(0.2), 0.182322, epsilon));
+    expect(math.approxEqAbs(f32, log1p_32(0.8923), 0.637793, epsilon));
+    expect(math.approxEqAbs(f32, log1p_32(1.5), 0.916291, epsilon));
+    expect(math.approxEqAbs(f32, log1p_32(37.45), 3.649359, epsilon));
+    expect(math.approxEqAbs(f32, log1p_32(89.123), 4.501175, epsilon));
+    expect(math.approxEqAbs(f32, log1p_32(123123.234375), 11.720949, epsilon));
 }
 
 test "math.log1p_64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, log1p_64(0.0), 0.0, epsilon));
-    expect(math.approxEq(f64, log1p_64(0.2), 0.182322, epsilon));
-    expect(math.approxEq(f64, log1p_64(0.8923), 0.637793, epsilon));
-    expect(math.approxEq(f64, log1p_64(1.5), 0.916291, epsilon));
-    expect(math.approxEq(f64, log1p_64(37.45), 3.649359, epsilon));
-    expect(math.approxEq(f64, log1p_64(89.123), 4.501175, epsilon));
-    expect(math.approxEq(f64, log1p_64(123123.234375), 11.720949, epsilon));
+    expect(math.approxEqAbs(f64, log1p_64(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f64, log1p_64(0.2), 0.182322, epsilon));
+    expect(math.approxEqAbs(f64, log1p_64(0.8923), 0.637793, epsilon));
+    expect(math.approxEqAbs(f64, log1p_64(1.5), 0.916291, epsilon));
+    expect(math.approxEqAbs(f64, log1p_64(37.45), 3.649359, epsilon));
+    expect(math.approxEqAbs(f64, log1p_64(89.123), 4.501175, epsilon));
+    expect(math.approxEqAbs(f64, log1p_64(123123.234375), 11.720949, epsilon));
 }
 
 test "math.log1p_32.special" {
lib/std/math/log2.zig
@@ -185,21 +185,21 @@ test "math.log2" {
 test "math.log2_32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, log2_32(0.2), -2.321928, epsilon));
-    expect(math.approxEq(f32, log2_32(0.8923), -0.164399, epsilon));
-    expect(math.approxEq(f32, log2_32(1.5), 0.584962, epsilon));
-    expect(math.approxEq(f32, log2_32(37.45), 5.226894, epsilon));
-    expect(math.approxEq(f32, log2_32(123123.234375), 16.909744, epsilon));
+    expect(math.approxEqAbs(f32, log2_32(0.2), -2.321928, epsilon));
+    expect(math.approxEqAbs(f32, log2_32(0.8923), -0.164399, epsilon));
+    expect(math.approxEqAbs(f32, log2_32(1.5), 0.584962, epsilon));
+    expect(math.approxEqAbs(f32, log2_32(37.45), 5.226894, epsilon));
+    expect(math.approxEqAbs(f32, log2_32(123123.234375), 16.909744, epsilon));
 }
 
 test "math.log2_64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, log2_64(0.2), -2.321928, epsilon));
-    expect(math.approxEq(f64, log2_64(0.8923), -0.164399, epsilon));
-    expect(math.approxEq(f64, log2_64(1.5), 0.584962, epsilon));
-    expect(math.approxEq(f64, log2_64(37.45), 5.226894, epsilon));
-    expect(math.approxEq(f64, log2_64(123123.234375), 16.909744, epsilon));
+    expect(math.approxEqAbs(f64, log2_64(0.2), -2.321928, epsilon));
+    expect(math.approxEqAbs(f64, log2_64(0.8923), -0.164399, epsilon));
+    expect(math.approxEqAbs(f64, log2_64(1.5), 0.584962, epsilon));
+    expect(math.approxEqAbs(f64, log2_64(37.45), 5.226894, epsilon));
+    expect(math.approxEqAbs(f64, log2_64(123123.234375), 16.909744, epsilon));
 }
 
 test "math.log2_32.special" {
lib/std/math/modf.zig
@@ -143,24 +143,24 @@ test "math.modf32" {
     var r: modf32_result = undefined;
 
     r = modf32(1.0);
-    expect(math.approxEq(f32, r.ipart, 1.0, epsilon));
-    expect(math.approxEq(f32, r.fpart, 0.0, epsilon));
+    expect(math.approxEqAbs(f32, r.ipart, 1.0, epsilon));
+    expect(math.approxEqAbs(f32, r.fpart, 0.0, epsilon));
 
     r = modf32(2.545);
-    expect(math.approxEq(f32, r.ipart, 2.0, epsilon));
-    expect(math.approxEq(f32, r.fpart, 0.545, epsilon));
+    expect(math.approxEqAbs(f32, r.ipart, 2.0, epsilon));
+    expect(math.approxEqAbs(f32, r.fpart, 0.545, epsilon));
 
     r = modf32(3.978123);
-    expect(math.approxEq(f32, r.ipart, 3.0, epsilon));
-    expect(math.approxEq(f32, r.fpart, 0.978123, epsilon));
+    expect(math.approxEqAbs(f32, r.ipart, 3.0, epsilon));
+    expect(math.approxEqAbs(f32, r.fpart, 0.978123, epsilon));
 
     r = modf32(43874.3);
-    expect(math.approxEq(f32, r.ipart, 43874, epsilon));
-    expect(math.approxEq(f32, r.fpart, 0.300781, epsilon));
+    expect(math.approxEqAbs(f32, r.ipart, 43874, epsilon));
+    expect(math.approxEqAbs(f32, r.fpart, 0.300781, epsilon));
 
     r = modf32(1234.340780);
-    expect(math.approxEq(f32, r.ipart, 1234, epsilon));
-    expect(math.approxEq(f32, r.fpart, 0.340820, epsilon));
+    expect(math.approxEqAbs(f32, r.ipart, 1234, epsilon));
+    expect(math.approxEqAbs(f32, r.fpart, 0.340820, epsilon));
 }
 
 test "math.modf64" {
@@ -168,24 +168,24 @@ test "math.modf64" {
     var r: modf64_result = undefined;
 
     r = modf64(1.0);
-    expect(math.approxEq(f64, r.ipart, 1.0, epsilon));
-    expect(math.approxEq(f64, r.fpart, 0.0, epsilon));
+    expect(math.approxEqAbs(f64, r.ipart, 1.0, epsilon));
+    expect(math.approxEqAbs(f64, r.fpart, 0.0, epsilon));
 
     r = modf64(2.545);
-    expect(math.approxEq(f64, r.ipart, 2.0, epsilon));
-    expect(math.approxEq(f64, r.fpart, 0.545, epsilon));
+    expect(math.approxEqAbs(f64, r.ipart, 2.0, epsilon));
+    expect(math.approxEqAbs(f64, r.fpart, 0.545, epsilon));
 
     r = modf64(3.978123);
-    expect(math.approxEq(f64, r.ipart, 3.0, epsilon));
-    expect(math.approxEq(f64, r.fpart, 0.978123, epsilon));
+    expect(math.approxEqAbs(f64, r.ipart, 3.0, epsilon));
+    expect(math.approxEqAbs(f64, r.fpart, 0.978123, epsilon));
 
     r = modf64(43874.3);
-    expect(math.approxEq(f64, r.ipart, 43874, epsilon));
-    expect(math.approxEq(f64, r.fpart, 0.3, epsilon));
+    expect(math.approxEqAbs(f64, r.ipart, 43874, epsilon));
+    expect(math.approxEqAbs(f64, r.fpart, 0.3, epsilon));
 
     r = modf64(1234.340780);
-    expect(math.approxEq(f64, r.ipart, 1234, epsilon));
-    expect(math.approxEq(f64, r.fpart, 0.340780, epsilon));
+    expect(math.approxEqAbs(f64, r.ipart, 1234, epsilon));
+    expect(math.approxEqAbs(f64, r.fpart, 0.340780, epsilon));
 }
 
 test "math.modf32.special" {
lib/std/math/pow.zig
@@ -191,19 +191,19 @@ fn isOddInteger(x: f64) bool {
 test "math.pow" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, pow(f32, 0.0, 3.3), 0.0, epsilon));
-    expect(math.approxEq(f32, pow(f32, 0.8923, 3.3), 0.686572, epsilon));
-    expect(math.approxEq(f32, pow(f32, 0.2, 3.3), 0.004936, epsilon));
-    expect(math.approxEq(f32, pow(f32, 1.5, 3.3), 3.811546, epsilon));
-    expect(math.approxEq(f32, pow(f32, 37.45, 3.3), 155736.703125, epsilon));
-    expect(math.approxEq(f32, pow(f32, 89.123, 3.3), 2722489.5, epsilon));
+    expect(math.approxEqAbs(f32, pow(f32, 0.0, 3.3), 0.0, epsilon));
+    expect(math.approxEqAbs(f32, pow(f32, 0.8923, 3.3), 0.686572, epsilon));
+    expect(math.approxEqAbs(f32, pow(f32, 0.2, 3.3), 0.004936, epsilon));
+    expect(math.approxEqAbs(f32, pow(f32, 1.5, 3.3), 3.811546, epsilon));
+    expect(math.approxEqAbs(f32, pow(f32, 37.45, 3.3), 155736.703125, epsilon));
+    expect(math.approxEqAbs(f32, pow(f32, 89.123, 3.3), 2722489.5, epsilon));
 
-    expect(math.approxEq(f64, pow(f64, 0.0, 3.3), 0.0, epsilon));
-    expect(math.approxEq(f64, pow(f64, 0.8923, 3.3), 0.686572, epsilon));
-    expect(math.approxEq(f64, pow(f64, 0.2, 3.3), 0.004936, epsilon));
-    expect(math.approxEq(f64, pow(f64, 1.5, 3.3), 3.811546, epsilon));
-    expect(math.approxEq(f64, pow(f64, 37.45, 3.3), 155736.7160616, epsilon));
-    expect(math.approxEq(f64, pow(f64, 89.123, 3.3), 2722490.231436, epsilon));
+    expect(math.approxEqAbs(f64, pow(f64, 0.0, 3.3), 0.0, epsilon));
+    expect(math.approxEqAbs(f64, pow(f64, 0.8923, 3.3), 0.686572, epsilon));
+    expect(math.approxEqAbs(f64, pow(f64, 0.2, 3.3), 0.004936, epsilon));
+    expect(math.approxEqAbs(f64, pow(f64, 1.5, 3.3), 3.811546, epsilon));
+    expect(math.approxEqAbs(f64, pow(f64, 37.45, 3.3), 155736.7160616, epsilon));
+    expect(math.approxEqAbs(f64, pow(f64, 89.123, 3.3), 2722490.231436, epsilon));
 }
 
 test "math.pow.special" {
@@ -230,8 +230,8 @@ test "math.pow.special" {
     expect(pow(f32, -0.0, 1.0) == -0.0);
     expect(pow(f32, 0.0, 2.0) == 0.0);
     expect(pow(f32, -0.0, 2.0) == 0.0);
-    expect(math.approxEq(f32, pow(f32, -1.0, math.inf(f32)), 1.0, epsilon));
-    expect(math.approxEq(f32, pow(f32, -1.0, -math.inf(f32)), 1.0, epsilon));
+    expect(math.approxEqAbs(f32, pow(f32, -1.0, math.inf(f32)), 1.0, epsilon));
+    expect(math.approxEqAbs(f32, pow(f32, -1.0, -math.inf(f32)), 1.0, epsilon));
     expect(math.isPositiveInf(pow(f32, 1.2, math.inf(f32))));
     expect(math.isPositiveInf(pow(f32, -1.2, math.inf(f32))));
     expect(pow(f32, 1.2, -math.inf(f32)) == 0.0);
lib/std/math/sin.zig
@@ -97,25 +97,25 @@ test "math.sin" {
 test "math.sin32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, sin_(f32, 0.0), 0.0, epsilon));
-    expect(math.approxEq(f32, sin_(f32, 0.2), 0.198669, epsilon));
-    expect(math.approxEq(f32, sin_(f32, 0.8923), 0.778517, epsilon));
-    expect(math.approxEq(f32, sin_(f32, 1.5), 0.997495, epsilon));
-    expect(math.approxEq(f32, sin_(f32, -1.5), -0.997495, epsilon));
-    expect(math.approxEq(f32, sin_(f32, 37.45), -0.246544, epsilon));
-    expect(math.approxEq(f32, sin_(f32, 89.123), 0.916166, epsilon));
+    expect(math.approxEqAbs(f32, sin_(f32, 0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f32, sin_(f32, 0.2), 0.198669, epsilon));
+    expect(math.approxEqAbs(f32, sin_(f32, 0.8923), 0.778517, epsilon));
+    expect(math.approxEqAbs(f32, sin_(f32, 1.5), 0.997495, epsilon));
+    expect(math.approxEqAbs(f32, sin_(f32, -1.5), -0.997495, epsilon));
+    expect(math.approxEqAbs(f32, sin_(f32, 37.45), -0.246544, epsilon));
+    expect(math.approxEqAbs(f32, sin_(f32, 89.123), 0.916166, epsilon));
 }
 
 test "math.sin64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, sin_(f64, 0.0), 0.0, epsilon));
-    expect(math.approxEq(f64, sin_(f64, 0.2), 0.198669, epsilon));
-    expect(math.approxEq(f64, sin_(f64, 0.8923), 0.778517, epsilon));
-    expect(math.approxEq(f64, sin_(f64, 1.5), 0.997495, epsilon));
-    expect(math.approxEq(f64, sin_(f64, -1.5), -0.997495, epsilon));
-    expect(math.approxEq(f64, sin_(f64, 37.45), -0.246543, epsilon));
-    expect(math.approxEq(f64, sin_(f64, 89.123), 0.916166, epsilon));
+    expect(math.approxEqAbs(f64, sin_(f64, 0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f64, sin_(f64, 0.2), 0.198669, epsilon));
+    expect(math.approxEqAbs(f64, sin_(f64, 0.8923), 0.778517, epsilon));
+    expect(math.approxEqAbs(f64, sin_(f64, 1.5), 0.997495, epsilon));
+    expect(math.approxEqAbs(f64, sin_(f64, -1.5), -0.997495, epsilon));
+    expect(math.approxEqAbs(f64, sin_(f64, 37.45), -0.246543, epsilon));
+    expect(math.approxEqAbs(f64, sin_(f64, 89.123), 0.916166, epsilon));
 }
 
 test "math.sin32.special" {
lib/std/math/sinh.zig
@@ -105,27 +105,27 @@ test "math.sinh" {
 test "math.sinh32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, sinh32(0.0), 0.0, epsilon));
-    expect(math.approxEq(f32, sinh32(0.2), 0.201336, epsilon));
-    expect(math.approxEq(f32, sinh32(0.8923), 1.015512, epsilon));
-    expect(math.approxEq(f32, sinh32(1.5), 2.129279, epsilon));
-    expect(math.approxEq(f32, sinh32(-0.0), -0.0, epsilon));
-    expect(math.approxEq(f32, sinh32(-0.2), -0.201336, epsilon));
-    expect(math.approxEq(f32, sinh32(-0.8923), -1.015512, epsilon));
-    expect(math.approxEq(f32, sinh32(-1.5), -2.129279, epsilon));
+    expect(math.approxEqAbs(f32, sinh32(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f32, sinh32(0.2), 0.201336, epsilon));
+    expect(math.approxEqAbs(f32, sinh32(0.8923), 1.015512, epsilon));
+    expect(math.approxEqAbs(f32, sinh32(1.5), 2.129279, epsilon));
+    expect(math.approxEqAbs(f32, sinh32(-0.0), -0.0, epsilon));
+    expect(math.approxEqAbs(f32, sinh32(-0.2), -0.201336, epsilon));
+    expect(math.approxEqAbs(f32, sinh32(-0.8923), -1.015512, epsilon));
+    expect(math.approxEqAbs(f32, sinh32(-1.5), -2.129279, epsilon));
 }
 
 test "math.sinh64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, sinh64(0.0), 0.0, epsilon));
-    expect(math.approxEq(f64, sinh64(0.2), 0.201336, epsilon));
-    expect(math.approxEq(f64, sinh64(0.8923), 1.015512, epsilon));
-    expect(math.approxEq(f64, sinh64(1.5), 2.129279, epsilon));
-    expect(math.approxEq(f64, sinh64(-0.0), -0.0, epsilon));
-    expect(math.approxEq(f64, sinh64(-0.2), -0.201336, epsilon));
-    expect(math.approxEq(f64, sinh64(-0.8923), -1.015512, epsilon));
-    expect(math.approxEq(f64, sinh64(-1.5), -2.129279, epsilon));
+    expect(math.approxEqAbs(f64, sinh64(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f64, sinh64(0.2), 0.201336, epsilon));
+    expect(math.approxEqAbs(f64, sinh64(0.8923), 1.015512, epsilon));
+    expect(math.approxEqAbs(f64, sinh64(1.5), 2.129279, epsilon));
+    expect(math.approxEqAbs(f64, sinh64(-0.0), -0.0, epsilon));
+    expect(math.approxEqAbs(f64, sinh64(-0.2), -0.201336, epsilon));
+    expect(math.approxEqAbs(f64, sinh64(-0.8923), -1.015512, epsilon));
+    expect(math.approxEqAbs(f64, sinh64(-1.5), -2.129279, epsilon));
 }
 
 test "math.sinh32.special" {
lib/std/math/tan.zig
@@ -87,23 +87,23 @@ test "math.tan" {
 test "math.tan32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, tan_(f32, 0.0), 0.0, epsilon));
-    expect(math.approxEq(f32, tan_(f32, 0.2), 0.202710, epsilon));
-    expect(math.approxEq(f32, tan_(f32, 0.8923), 1.240422, epsilon));
-    expect(math.approxEq(f32, tan_(f32, 1.5), 14.101420, epsilon));
-    expect(math.approxEq(f32, tan_(f32, 37.45), -0.254397, epsilon));
-    expect(math.approxEq(f32, tan_(f32, 89.123), 2.285852, epsilon));
+    expect(math.approxEqAbs(f32, tan_(f32, 0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f32, tan_(f32, 0.2), 0.202710, epsilon));
+    expect(math.approxEqAbs(f32, tan_(f32, 0.8923), 1.240422, epsilon));
+    expect(math.approxEqAbs(f32, tan_(f32, 1.5), 14.101420, epsilon));
+    expect(math.approxEqAbs(f32, tan_(f32, 37.45), -0.254397, epsilon));
+    expect(math.approxEqAbs(f32, tan_(f32, 89.123), 2.285852, epsilon));
 }
 
 test "math.tan64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, tan_(f64, 0.0), 0.0, epsilon));
-    expect(math.approxEq(f64, tan_(f64, 0.2), 0.202710, epsilon));
-    expect(math.approxEq(f64, tan_(f64, 0.8923), 1.240422, epsilon));
-    expect(math.approxEq(f64, tan_(f64, 1.5), 14.101420, epsilon));
-    expect(math.approxEq(f64, tan_(f64, 37.45), -0.254397, epsilon));
-    expect(math.approxEq(f64, tan_(f64, 89.123), 2.2858376, epsilon));
+    expect(math.approxEqAbs(f64, tan_(f64, 0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f64, tan_(f64, 0.2), 0.202710, epsilon));
+    expect(math.approxEqAbs(f64, tan_(f64, 0.8923), 1.240422, epsilon));
+    expect(math.approxEqAbs(f64, tan_(f64, 1.5), 14.101420, epsilon));
+    expect(math.approxEqAbs(f64, tan_(f64, 37.45), -0.254397, epsilon));
+    expect(math.approxEqAbs(f64, tan_(f64, 89.123), 2.2858376, epsilon));
 }
 
 test "math.tan32.special" {
lib/std/math/tanh.zig
@@ -131,21 +131,21 @@ test "math.tanh" {
 test "math.tanh32" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f32, tanh32(0.0), 0.0, epsilon));
-    expect(math.approxEq(f32, tanh32(0.2), 0.197375, epsilon));
-    expect(math.approxEq(f32, tanh32(0.8923), 0.712528, epsilon));
-    expect(math.approxEq(f32, tanh32(1.5), 0.905148, epsilon));
-    expect(math.approxEq(f32, tanh32(37.45), 1.0, epsilon));
+    expect(math.approxEqAbs(f32, tanh32(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f32, tanh32(0.2), 0.197375, epsilon));
+    expect(math.approxEqAbs(f32, tanh32(0.8923), 0.712528, epsilon));
+    expect(math.approxEqAbs(f32, tanh32(1.5), 0.905148, epsilon));
+    expect(math.approxEqAbs(f32, tanh32(37.45), 1.0, epsilon));
 }
 
 test "math.tanh64" {
     const epsilon = 0.000001;
 
-    expect(math.approxEq(f64, tanh64(0.0), 0.0, epsilon));
-    expect(math.approxEq(f64, tanh64(0.2), 0.197375, epsilon));
-    expect(math.approxEq(f64, tanh64(0.8923), 0.712528, epsilon));
-    expect(math.approxEq(f64, tanh64(1.5), 0.905148, epsilon));
-    expect(math.approxEq(f64, tanh64(37.45), 1.0, epsilon));
+    expect(math.approxEqAbs(f64, tanh64(0.0), 0.0, epsilon));
+    expect(math.approxEqAbs(f64, tanh64(0.2), 0.197375, epsilon));
+    expect(math.approxEqAbs(f64, tanh64(0.8923), 0.712528, epsilon));
+    expect(math.approxEqAbs(f64, tanh64(1.5), 0.905148, epsilon));
+    expect(math.approxEqAbs(f64, tanh64(37.45), 1.0, epsilon));
 }
 
 test "math.tanh32.special" {
lib/std/special/c.zig
@@ -912,14 +912,14 @@ test "sqrt" {
     const epsilon = 0.000001;
 
     std.testing.expect(sqrt(0.0) == 0.0);
-    std.testing.expect(std.math.approxEq(f64, sqrt(2.0), 1.414214, epsilon));
-    std.testing.expect(std.math.approxEq(f64, sqrt(3.6), 1.897367, epsilon));
+    std.testing.expect(std.math.approxEqAbs(f64, sqrt(2.0), 1.414214, epsilon));
+    std.testing.expect(std.math.approxEqAbs(f64, sqrt(3.6), 1.897367, epsilon));
     std.testing.expect(sqrt(4.0) == 2.0);
-    std.testing.expect(std.math.approxEq(f64, sqrt(7.539840), 2.745877, epsilon));
-    std.testing.expect(std.math.approxEq(f64, sqrt(19.230934), 4.385309, epsilon));
+    std.testing.expect(std.math.approxEqAbs(f64, sqrt(7.539840), 2.745877, epsilon));
+    std.testing.expect(std.math.approxEqAbs(f64, sqrt(19.230934), 4.385309, epsilon));
     std.testing.expect(sqrt(64.0) == 8.0);
-    std.testing.expect(std.math.approxEq(f64, sqrt(64.1), 8.006248, epsilon));
-    std.testing.expect(std.math.approxEq(f64, sqrt(8942.230469), 94.563367, epsilon));
+    std.testing.expect(std.math.approxEqAbs(f64, sqrt(64.1), 8.006248, epsilon));
+    std.testing.expect(std.math.approxEqAbs(f64, sqrt(8942.230469), 94.563367, epsilon));
 }
 
 test "sqrt special" {
@@ -1010,14 +1010,14 @@ test "sqrtf" {
     const epsilon = 0.000001;
 
     std.testing.expect(sqrtf(0.0) == 0.0);
-    std.testing.expect(std.math.approxEq(f32, sqrtf(2.0), 1.414214, epsilon));
-    std.testing.expect(std.math.approxEq(f32, sqrtf(3.6), 1.897367, epsilon));
+    std.testing.expect(std.math.approxEqAbs(f32, sqrtf(2.0), 1.414214, epsilon));
+    std.testing.expect(std.math.approxEqAbs(f32, sqrtf(3.6), 1.897367, epsilon));
     std.testing.expect(sqrtf(4.0) == 2.0);
-    std.testing.expect(std.math.approxEq(f32, sqrtf(7.539840), 2.745877, epsilon));
-    std.testing.expect(std.math.approxEq(f32, sqrtf(19.230934), 4.385309, epsilon));
+    std.testing.expect(std.math.approxEqAbs(f32, sqrtf(7.539840), 2.745877, epsilon));
+    std.testing.expect(std.math.approxEqAbs(f32, sqrtf(19.230934), 4.385309, epsilon));
     std.testing.expect(sqrtf(64.0) == 8.0);
-    std.testing.expect(std.math.approxEq(f32, sqrtf(64.1), 8.006248, epsilon));
-    std.testing.expect(std.math.approxEq(f32, sqrtf(8942.230469), 94.563370, epsilon));
+    std.testing.expect(std.math.approxEqAbs(f32, sqrtf(64.1), 8.006248, epsilon));
+    std.testing.expect(std.math.approxEqAbs(f32, sqrtf(8942.230469), 94.563370, epsilon));
 }
 
 test "sqrtf special" {
lib/std/math.zig
@@ -64,6 +64,8 @@ pub const f16_max = 65504;
 pub const f16_epsilon = 0.0009765625; // 2**-10
 pub const f16_toint = 1.0 / f16_epsilon;
 
+pub const epsilon = @import("math/epsilon.zig").epsilon;
+
 pub const nan_u16 = @as(u16, 0x7C01);
 pub const nan_f16 = @bitCast(f16, nan_u16);
 
@@ -104,9 +106,92 @@ pub const nan = @import("math/nan.zig").nan;
 pub const snan = @import("math/nan.zig").snan;
 pub const inf = @import("math/inf.zig").inf;
 
-pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool {
+/// Performs an approximate comparison of two floating point values `x` and `y`.
+/// Returns true if the absolute difference between them is less or equal than
+/// the specified tolerance.
+///
+/// The `tolerance` parameter is the absolute tolerance used when determining if
+/// the two numbers are close enough, a good value for this parameter is a small
+/// multiple of `epsilon(T)`.
+///
+/// Note that this function is recommended for for comparing small numbers
+/// around zero, using `approxEqRel` is suggested otherwise.
+///
+/// NaN values are never considered equal to any value.
+pub fn approxEqAbs(comptime T: type, x: T, y: T, tolerance: T) bool {
+    assert(@typeInfo(T) == .Float);
+    assert(tolerance >= 0);
+
+    // Fast path for equal values (and signed zeros and infinites).
+    if (x == y)
+        return true;
+
+    if (isNan(x) or isNan(y))
+        return false;
+
+    return fabs(x - y) <= tolerance;
+}
+
+/// Performs an approximate comparison of two floating point values `x` and `y`.
+/// Returns true if the absolute difference between them is less or equal than
+/// `max(|x|, |y|) * tolerance`, where `tolerance` is a positive number greater
+/// than zero.
+///
+/// The `tolerance` parameter is the relative tolerance used when determining if
+/// the two numbers are close enough, a good value for this parameter is usually
+/// `sqrt(epsilon(T))`, meaning that the two numbers are considered equal if at
+/// least half of the digits are equal.
+///
+/// Note that for comparisons of small numbers around zero this function won't
+/// give meaningful results, use `approxEqAbs` instead.
+///
+/// NaN values are never considered equal to any value.
+pub fn approxEqRel(comptime T: type, x: T, y: T, tolerance: T) bool {
     assert(@typeInfo(T) == .Float);
-    return fabs(x - y) < epsilon;
+    assert(tolerance > 0);
+
+    // Fast path for equal values (and signed zeros and infinites).
+    if (x == y)
+        return true;
+
+    if (isNan(x) or isNan(y))
+        return false;
+
+    return fabs(x - y) <= max(fabs(x), fabs(y)) * tolerance;
+}
+
+/// Deprecated, use `approxEqAbs` or `approxEqRel`.
+pub const approxEq = approxEqAbs;
+
+test "approxEqAbs and approxEqRel" {
+    inline for ([_]type{ f16, f32, f64, f128 }) |T| {
+        const eps_value = comptime epsilon(T);
+        const sqrt_eps_value = comptime sqrt(eps_value);
+        const nan_value = comptime nan(T);
+        const inf_value = comptime inf(T);
+        const min_value: T = switch (T) {
+            f16 => f16_min,
+            f32 => f32_min,
+            f64 => f64_min,
+            f128 => f128_min,
+            else => unreachable,
+        };
+
+        testing.expect(approxEqAbs(T, 0.0, 0.0, eps_value));
+        testing.expect(approxEqAbs(T, -0.0, -0.0, eps_value));
+        testing.expect(approxEqAbs(T, 0.0, -0.0, eps_value));
+        testing.expect(approxEqRel(T, 1.0, 1.0, sqrt_eps_value));
+        testing.expect(!approxEqRel(T, 1.0, 0.0, sqrt_eps_value));
+        testing.expect(!approxEqAbs(T, 1.0 + 2 * epsilon(T), 1.0, eps_value));
+        testing.expect(approxEqAbs(T, 1.0 + 1 * epsilon(T), 1.0, eps_value));
+        testing.expect(!approxEqRel(T, 1.0, nan_value, sqrt_eps_value));
+        testing.expect(!approxEqRel(T, nan_value, nan_value, sqrt_eps_value));
+        testing.expect(approxEqRel(T, inf_value, inf_value, sqrt_eps_value));
+        testing.expect(approxEqRel(T, min_value, min_value, sqrt_eps_value));
+        testing.expect(approxEqRel(T, -min_value, -min_value, sqrt_eps_value));
+        testing.expect(approxEqAbs(T, min_value, 0.0, eps_value * 2));
+        testing.expect(approxEqAbs(T, -min_value, 0.0, eps_value * 2));
+    }
 }
 
 pub fn doNotOptimizeAway(value: anytype) void {
test/stage1/behavior/floatop.zig
@@ -21,7 +21,7 @@ fn testSqrt() void {
         var a: f32 = 9;
         expect(@sqrt(a) == 3);
         var b: f32 = 1.1;
-        expect(math.approxEq(f32, @sqrt(b), 1.0488088481701516, epsilon));
+        expect(math.approxEqAbs(f32, @sqrt(b), 1.0488088481701516, epsilon));
     }
     {
         var a: f64 = 25;
@@ -39,24 +39,24 @@ fn testSqrt() void {
     {
         var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 };
         var result = @sqrt(v);
-        expect(math.approxEq(f32, @sqrt(@as(f32, 1.1)), result[0], epsilon));
-        expect(math.approxEq(f32, @sqrt(@as(f32, 2.2)), result[1], epsilon));
-        expect(math.approxEq(f32, @sqrt(@as(f32, 3.3)), result[2], epsilon));
-        expect(math.approxEq(f32, @sqrt(@as(f32, 4.4)), result[3], epsilon));
+        expect(math.approxEqAbs(f32, @sqrt(@as(f32, 1.1)), result[0], epsilon));
+        expect(math.approxEqAbs(f32, @sqrt(@as(f32, 2.2)), result[1], epsilon));
+        expect(math.approxEqAbs(f32, @sqrt(@as(f32, 3.3)), result[2], epsilon));
+        expect(math.approxEqAbs(f32, @sqrt(@as(f32, 4.4)), result[3], epsilon));
     }
 }
 
 test "more @sqrt f16 tests" {
     // TODO these are not all passing at comptime
     expect(@sqrt(@as(f16, 0.0)) == 0.0);
-    expect(math.approxEq(f16, @sqrt(@as(f16, 2.0)), 1.414214, epsilon));
-    expect(math.approxEq(f16, @sqrt(@as(f16, 3.6)), 1.897367, epsilon));
+    expect(math.approxEqAbs(f16, @sqrt(@as(f16, 2.0)), 1.414214, epsilon));
+    expect(math.approxEqAbs(f16, @sqrt(@as(f16, 3.6)), 1.897367, epsilon));
     expect(@sqrt(@as(f16, 4.0)) == 2.0);
-    expect(math.approxEq(f16, @sqrt(@as(f16, 7.539840)), 2.745877, epsilon));
-    expect(math.approxEq(f16, @sqrt(@as(f16, 19.230934)), 4.385309, epsilon));
+    expect(math.approxEqAbs(f16, @sqrt(@as(f16, 7.539840)), 2.745877, epsilon));
+    expect(math.approxEqAbs(f16, @sqrt(@as(f16, 19.230934)), 4.385309, epsilon));
     expect(@sqrt(@as(f16, 64.0)) == 8.0);
-    expect(math.approxEq(f16, @sqrt(@as(f16, 64.1)), 8.006248, epsilon));
-    expect(math.approxEq(f16, @sqrt(@as(f16, 8942.230469)), 94.563370, epsilon));
+    expect(math.approxEqAbs(f16, @sqrt(@as(f16, 64.1)), 8.006248, epsilon));
+    expect(math.approxEqAbs(f16, @sqrt(@as(f16, 8942.230469)), 94.563370, epsilon));
 
     // special cases
     expect(math.isPositiveInf(@sqrt(@as(f16, math.inf(f16)))));
@@ -89,10 +89,10 @@ fn testSin() void {
     {
         var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 };
         var result = @sin(v);
-        expect(math.approxEq(f32, @sin(@as(f32, 1.1)), result[0], epsilon));
-        expect(math.approxEq(f32, @sin(@as(f32, 2.2)), result[1], epsilon));
-        expect(math.approxEq(f32, @sin(@as(f32, 3.3)), result[2], epsilon));
-        expect(math.approxEq(f32, @sin(@as(f32, 4.4)), result[3], epsilon));
+        expect(math.approxEqAbs(f32, @sin(@as(f32, 1.1)), result[0], epsilon));
+        expect(math.approxEqAbs(f32, @sin(@as(f32, 2.2)), result[1], epsilon));
+        expect(math.approxEqAbs(f32, @sin(@as(f32, 3.3)), result[2], epsilon));
+        expect(math.approxEqAbs(f32, @sin(@as(f32, 4.4)), result[3], epsilon));
     }
 }
 
@@ -119,10 +119,10 @@ fn testCos() void {
     {
         var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 };
         var result = @cos(v);
-        expect(math.approxEq(f32, @cos(@as(f32, 1.1)), result[0], epsilon));
-        expect(math.approxEq(f32, @cos(@as(f32, 2.2)), result[1], epsilon));
-        expect(math.approxEq(f32, @cos(@as(f32, 3.3)), result[2], epsilon));
-        expect(math.approxEq(f32, @cos(@as(f32, 4.4)), result[3], epsilon));
+        expect(math.approxEqAbs(f32, @cos(@as(f32, 1.1)), result[0], epsilon));
+        expect(math.approxEqAbs(f32, @cos(@as(f32, 2.2)), result[1], epsilon));
+        expect(math.approxEqAbs(f32, @cos(@as(f32, 3.3)), result[2], epsilon));
+        expect(math.approxEqAbs(f32, @cos(@as(f32, 4.4)), result[3], epsilon));
     }
 }
 
@@ -149,10 +149,10 @@ fn testExp() void {
     {
         var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
         var result = @exp(v);
-        expect(math.approxEq(f32, @exp(@as(f32, 1.1)), result[0], epsilon));
-        expect(math.approxEq(f32, @exp(@as(f32, 2.2)), result[1], epsilon));
-        expect(math.approxEq(f32, @exp(@as(f32, 0.3)), result[2], epsilon));
-        expect(math.approxEq(f32, @exp(@as(f32, 0.4)), result[3], epsilon));
+        expect(math.approxEqAbs(f32, @exp(@as(f32, 1.1)), result[0], epsilon));
+        expect(math.approxEqAbs(f32, @exp(@as(f32, 2.2)), result[1], epsilon));
+        expect(math.approxEqAbs(f32, @exp(@as(f32, 0.3)), result[2], epsilon));
+        expect(math.approxEqAbs(f32, @exp(@as(f32, 0.4)), result[3], epsilon));
     }
 }
 
@@ -179,10 +179,10 @@ fn testExp2() void {
     {
         var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
         var result = @exp2(v);
-        expect(math.approxEq(f32, @exp2(@as(f32, 1.1)), result[0], epsilon));
-        expect(math.approxEq(f32, @exp2(@as(f32, 2.2)), result[1], epsilon));
-        expect(math.approxEq(f32, @exp2(@as(f32, 0.3)), result[2], epsilon));
-        expect(math.approxEq(f32, @exp2(@as(f32, 0.4)), result[3], epsilon));
+        expect(math.approxEqAbs(f32, @exp2(@as(f32, 1.1)), result[0], epsilon));
+        expect(math.approxEqAbs(f32, @exp2(@as(f32, 2.2)), result[1], epsilon));
+        expect(math.approxEqAbs(f32, @exp2(@as(f32, 0.3)), result[2], epsilon));
+        expect(math.approxEqAbs(f32, @exp2(@as(f32, 0.4)), result[3], epsilon));
     }
 }
 
@@ -198,7 +198,7 @@ fn testLog() void {
     // https://github.com/ziglang/zig/issues/4026
     {
         var a: f16 = e;
-        expect(math.approxEq(f16, @log(a), 1, epsilon));
+        expect(math.approxEqAbs(f16, @log(a), 1, epsilon));
     }
     {
         var a: f32 = e;
@@ -211,10 +211,10 @@ fn testLog() void {
     {
         var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
         var result = @log(v);
-        expect(math.approxEq(f32, @log(@as(f32, 1.1)), result[0], epsilon));
-        expect(math.approxEq(f32, @log(@as(f32, 2.2)), result[1], epsilon));
-        expect(math.approxEq(f32, @log(@as(f32, 0.3)), result[2], epsilon));
-        expect(math.approxEq(f32, @log(@as(f32, 0.4)), result[3], epsilon));
+        expect(math.approxEqAbs(f32, @log(@as(f32, 1.1)), result[0], epsilon));
+        expect(math.approxEqAbs(f32, @log(@as(f32, 2.2)), result[1], epsilon));
+        expect(math.approxEqAbs(f32, @log(@as(f32, 0.3)), result[2], epsilon));
+        expect(math.approxEqAbs(f32, @log(@as(f32, 0.4)), result[3], epsilon));
     }
 }
 
@@ -241,10 +241,10 @@ fn testLog2() void {
     {
         var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
         var result = @log2(v);
-        expect(math.approxEq(f32, @log2(@as(f32, 1.1)), result[0], epsilon));
-        expect(math.approxEq(f32, @log2(@as(f32, 2.2)), result[1], epsilon));
-        expect(math.approxEq(f32, @log2(@as(f32, 0.3)), result[2], epsilon));
-        expect(math.approxEq(f32, @log2(@as(f32, 0.4)), result[3], epsilon));
+        expect(math.approxEqAbs(f32, @log2(@as(f32, 1.1)), result[0], epsilon));
+        expect(math.approxEqAbs(f32, @log2(@as(f32, 2.2)), result[1], epsilon));
+        expect(math.approxEqAbs(f32, @log2(@as(f32, 0.3)), result[2], epsilon));
+        expect(math.approxEqAbs(f32, @log2(@as(f32, 0.4)), result[3], epsilon));
     }
 }
 
@@ -271,10 +271,10 @@ fn testLog10() void {
     {
         var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
         var result = @log10(v);
-        expect(math.approxEq(f32, @log10(@as(f32, 1.1)), result[0], epsilon));
-        expect(math.approxEq(f32, @log10(@as(f32, 2.2)), result[1], epsilon));
-        expect(math.approxEq(f32, @log10(@as(f32, 0.3)), result[2], epsilon));
-        expect(math.approxEq(f32, @log10(@as(f32, 0.4)), result[3], epsilon));
+        expect(math.approxEqAbs(f32, @log10(@as(f32, 1.1)), result[0], epsilon));
+        expect(math.approxEqAbs(f32, @log10(@as(f32, 2.2)), result[1], epsilon));
+        expect(math.approxEqAbs(f32, @log10(@as(f32, 0.3)), result[2], epsilon));
+        expect(math.approxEqAbs(f32, @log10(@as(f32, 0.4)), result[3], epsilon));
     }
 }
 
@@ -307,10 +307,10 @@ fn testFabs() void {
     {
         var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
         var result = @fabs(v);
-        expect(math.approxEq(f32, @fabs(@as(f32, 1.1)), result[0], epsilon));
-        expect(math.approxEq(f32, @fabs(@as(f32, -2.2)), result[1], epsilon));
-        expect(math.approxEq(f32, @fabs(@as(f32, 0.3)), result[2], epsilon));
-        expect(math.approxEq(f32, @fabs(@as(f32, -0.4)), result[3], epsilon));
+        expect(math.approxEqAbs(f32, @fabs(@as(f32, 1.1)), result[0], epsilon));
+        expect(math.approxEqAbs(f32, @fabs(@as(f32, -2.2)), result[1], epsilon));
+        expect(math.approxEqAbs(f32, @fabs(@as(f32, 0.3)), result[2], epsilon));
+        expect(math.approxEqAbs(f32, @fabs(@as(f32, -0.4)), result[3], epsilon));
     }
 }
 
@@ -337,10 +337,10 @@ fn testFloor() void {
     {
         var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
         var result = @floor(v);
-        expect(math.approxEq(f32, @floor(@as(f32, 1.1)), result[0], epsilon));
-        expect(math.approxEq(f32, @floor(@as(f32, -2.2)), result[1], epsilon));
-        expect(math.approxEq(f32, @floor(@as(f32, 0.3)), result[2], epsilon));
-        expect(math.approxEq(f32, @floor(@as(f32, -0.4)), result[3], epsilon));
+        expect(math.approxEqAbs(f32, @floor(@as(f32, 1.1)), result[0], epsilon));
+        expect(math.approxEqAbs(f32, @floor(@as(f32, -2.2)), result[1], epsilon));
+        expect(math.approxEqAbs(f32, @floor(@as(f32, 0.3)), result[2], epsilon));
+        expect(math.approxEqAbs(f32, @floor(@as(f32, -0.4)), result[3], epsilon));
     }
 }
 
@@ -367,10 +367,10 @@ fn testCeil() void {
     {
         var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
         var result = @ceil(v);
-        expect(math.approxEq(f32, @ceil(@as(f32, 1.1)), result[0], epsilon));
-        expect(math.approxEq(f32, @ceil(@as(f32, -2.2)), result[1], epsilon));
-        expect(math.approxEq(f32, @ceil(@as(f32, 0.3)), result[2], epsilon));
-        expect(math.approxEq(f32, @ceil(@as(f32, -0.4)), result[3], epsilon));
+        expect(math.approxEqAbs(f32, @ceil(@as(f32, 1.1)), result[0], epsilon));
+        expect(math.approxEqAbs(f32, @ceil(@as(f32, -2.2)), result[1], epsilon));
+        expect(math.approxEqAbs(f32, @ceil(@as(f32, 0.3)), result[2], epsilon));
+        expect(math.approxEqAbs(f32, @ceil(@as(f32, -0.4)), result[3], epsilon));
     }
 }
 
@@ -397,10 +397,10 @@ fn testTrunc() void {
     {
         var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
         var result = @trunc(v);
-        expect(math.approxEq(f32, @trunc(@as(f32, 1.1)), result[0], epsilon));
-        expect(math.approxEq(f32, @trunc(@as(f32, -2.2)), result[1], epsilon));
-        expect(math.approxEq(f32, @trunc(@as(f32, 0.3)), result[2], epsilon));
-        expect(math.approxEq(f32, @trunc(@as(f32, -0.4)), result[3], epsilon));
+        expect(math.approxEqAbs(f32, @trunc(@as(f32, 1.1)), result[0], epsilon));
+        expect(math.approxEqAbs(f32, @trunc(@as(f32, -2.2)), result[1], epsilon));
+        expect(math.approxEqAbs(f32, @trunc(@as(f32, 0.3)), result[2], epsilon));
+        expect(math.approxEqAbs(f32, @trunc(@as(f32, -0.4)), result[3], epsilon));
     }
 }