From f94964cd057ace39caa05c10d9626ac6a4beb23b Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Wed, 1 May 2019 18:12:16 +1200 Subject: [PATCH 1/3] std.math: Add upstream changes/fixes and simplify go derived code This also starts the documentation effort for the math/ subdirectory. The intent is to use this as a somewhat representative test-case for any work on the documentation generator. --- std/math/cos.zig | 146 ++++++++++++++---------------------------- std/math/fma.zig | 10 ++- std/math/pow.zig | 96 ++++++++++++++++------------ std/math/sin.zig | 160 +++++++++++++++-------------------------------- std/math/tan.zig | 154 +++++++++++++++------------------------------ 5 files changed, 214 insertions(+), 352 deletions(-) diff --git a/std/math/cos.zig b/std/math/cos.zig index 947948289490af680df2a582cdb1cbce1ad73727..6d0cc80c315d8eabbc3ffc282d4b60d98a8e9007 100644 --- a/std/math/cos.zig +++ b/std/math/cos.zig @@ -1,18 +1,23 @@ -// Special Cases: +// Ported from go, which is licensed under a BSD-3 license. +// https://golang.org/LICENSE // -// - cos(+-inf) = nan -// - cos(nan) = nan +// https://golang.org/src/math/sin.go const builtin = @import("builtin"); const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns the cosine of the radian value x. +/// +/// Special Cases: +/// - cos(+-inf) = nan +/// - cos(nan) = nan pub fn cos(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { - f32 => cos32(x), - f64 => cos64(x), + f32 => cos_(f32, x), + f64 => cos_(f64, x), else => @compileError("cos not implemented for " ++ @typeName(T)), }; } @@ -33,14 +38,13 @@ const C3 = 2.48015872888517045348E-5; const C4 = -1.38888888888730564116E-3; const C5 = 4.16666666666665929218E-2; -// NOTE: This is taken from the go stdlib. The musl implementation is much more complex. -// -// This may have slight differences on some edge cases and may need to replaced if so. -fn cos32(x_: f32) f32 { - const pi4a = 7.85398125648498535156e-1; - const pi4b = 3.77489470793079817668E-8; - const pi4c = 2.69515142907905952645E-15; - const m4pi = 1.273239544735162542821171882678754627704620361328125; +const pi4a = 7.85398125648498535156e-1; +const pi4b = 3.77489470793079817668E-8; +const pi4c = 2.69515142907905952645E-15; +const m4pi = 1.273239544735162542821171882678754627704620361328125; + +fn cos_(comptime T: type, x_: T) T { + const I = @IntType(true, T.bit_count); var x = x_; if (math.isNan(x) or math.isInf(x)) { @@ -48,12 +52,10 @@ fn cos32(x_: f32) f32 { } var sign = false; - if (x < 0) { - x = -x; - } + x = math.fabs(x); var y = math.floor(x * m4pi); - var j = @floatToInt(i64, y); + var j = @floatToInt(I, y); if (j & 1 == 1) { j += 1; @@ -72,107 +74,51 @@ fn cos32(x_: f32) f32 { const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; const w = z * z; - const r = r: { - if (j == 1 or j == 2) { - break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))); - } else { - break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))); - } - }; - - if (sign) { - return -r; - } else { - return r; - } -} - -fn cos64(x_: f64) f64 { - const pi4a = 7.85398125648498535156e-1; - const pi4b = 3.77489470793079817668E-8; - const pi4c = 2.69515142907905952645E-15; - const m4pi = 1.273239544735162542821171882678754627704620361328125; - - var x = x_; - if (math.isNan(x) or math.isInf(x)) { - return math.nan(f64); - } - - var sign = false; - if (x < 0) { - x = -x; - } - - var y = math.floor(x * m4pi); - var j = @floatToInt(i64, y); - - if (j & 1 == 1) { - j += 1; - y += 1; - } + const r = if (j == 1 or j == 2) + z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))) + else + 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))); - j &= 7; - if (j > 3) { - j -= 4; - sign = !sign; - } - if (j > 1) { - sign = !sign; - } - - const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; - const w = z * z; - - const r = r: { - if (j == 1 or j == 2) { - break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))); - } else { - break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))); - } - }; - - if (sign) { - return -r; - } else { - return r; - } + return if (sign) -r else r; } test "math.cos" { - expect(cos(f32(0.0)) == cos32(0.0)); - expect(cos(f64(0.0)) == cos64(0.0)); + expect(cos(f32(0.0)) == cos_(f32, 0.0)); + expect(cos(f64(0.0)) == cos_(f64, 0.0)); } test "math.cos32" { const epsilon = 0.000001; - expect(math.approxEq(f32, cos32(0.0), 1.0, epsilon)); - expect(math.approxEq(f32, cos32(0.2), 0.980067, epsilon)); - expect(math.approxEq(f32, cos32(0.8923), 0.627623, epsilon)); - expect(math.approxEq(f32, cos32(1.5), 0.070737, epsilon)); - expect(math.approxEq(f32, cos32(37.45), 0.969132, epsilon)); - expect(math.approxEq(f32, cos32(89.123), 0.400798, epsilon)); + 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)); } test "math.cos64" { const epsilon = 0.000001; - expect(math.approxEq(f64, cos64(0.0), 1.0, epsilon)); - expect(math.approxEq(f64, cos64(0.2), 0.980067, epsilon)); - expect(math.approxEq(f64, cos64(0.8923), 0.627623, epsilon)); - expect(math.approxEq(f64, cos64(1.5), 0.070737, epsilon)); - expect(math.approxEq(f64, cos64(37.45), 0.969132, epsilon)); - expect(math.approxEq(f64, cos64(89.123), 0.40080, epsilon)); + 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)); } test "math.cos32.special" { - expect(math.isNan(cos32(math.inf(f32)))); - expect(math.isNan(cos32(-math.inf(f32)))); - expect(math.isNan(cos32(math.nan(f32)))); + expect(math.isNan(cos_(f32, math.inf(f32)))); + expect(math.isNan(cos_(f32, -math.inf(f32)))); + expect(math.isNan(cos_(f32, math.nan(f32)))); } test "math.cos64.special" { - expect(math.isNan(cos64(math.inf(f64)))); - expect(math.isNan(cos64(-math.inf(f64)))); - expect(math.isNan(cos64(math.nan(f64)))); + expect(math.isNan(cos_(f64, math.inf(f64)))); + expect(math.isNan(cos_(f64, -math.inf(f64)))); + expect(math.isNan(cos_(f64, math.nan(f64)))); } diff --git a/std/math/fma.zig b/std/math/fma.zig index a317bc96de08cc07a793223fd74145dba039da6e..19c306fa2a3b001f70b29856ba09699d86ec75b6 100644 --- a/std/math/fma.zig +++ b/std/math/fma.zig @@ -1,7 +1,14 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/math/fmaf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/fma.c + const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns x * y + z with a single rounding error. pub fn fma(comptime T: type, x: T, y: T, z: T) T { return switch (T) { f32 => fma32(x, y, z), @@ -16,7 +23,7 @@ fn fma32(x: f32, y: f32, z: f32) f32 { const u = @bitCast(u64, xy_z); const e = (u >> 52) & 0x7FF; - if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or xy_z - xy == z) { + if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or (xy_z - xy == z and xy_z - z == xy)) { return @floatCast(f32, xy_z); } else { // TODO: Handle inexact case with double-rounding @@ -24,6 +31,7 @@ fn fma32(x: f32, y: f32, z: f32) f32 { } } +// NOTE: Upstream fma.c has been rewritten completely to raise fp exceptions more accurately. fn fma64(x: f64, y: f64, z: f64) f64 { if (!math.isFinite(x) or !math.isFinite(y)) { return x * y + z; diff --git a/std/math/pow.zig b/std/math/pow.zig index 81bb2d95d5ab009f42d5184c3719951ae1b2f31b..18c9f80634eff38e7c7ae284c3e17e5c36314614 100644 --- a/std/math/pow.zig +++ b/std/math/pow.zig @@ -1,32 +1,36 @@ -// Special Cases: +// Ported from go, which is licensed under a BSD-3 license. +// https://golang.org/LICENSE // -// pow(x, +-0) = 1 for any x -// pow(1, y) = 1 for any y -// pow(x, 1) = x for any x -// pow(nan, y) = nan -// pow(x, nan) = nan -// pow(+-0, y) = +-inf for y an odd integer < 0 -// pow(+-0, -inf) = +inf -// pow(+-0, +inf) = +0 -// pow(+-0, y) = +inf for finite y < 0 and not an odd integer -// pow(+-0, y) = +-0 for y an odd integer > 0 -// pow(+-0, y) = +0 for finite y > 0 and not an odd integer -// pow(-1, +-inf) = 1 -// pow(x, +inf) = +inf for |x| > 1 -// pow(x, -inf) = +0 for |x| > 1 -// pow(x, +inf) = +0 for |x| < 1 -// pow(x, -inf) = +inf for |x| < 1 -// pow(+inf, y) = +inf for y > 0 -// pow(+inf, y) = +0 for y < 0 -// pow(-inf, y) = pow(-0, -y) -// pow(x, y) = nan for finite x < 0 and finite non-integer y +// https://golang.org/src/math/pow.go const builtin = @import("builtin"); const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; -// This implementation is taken from the go stlib, musl is a bit more complex. +/// Returns x raised to the power of y (x^y). +/// +/// Special Cases: +/// - pow(x, +-0) = 1 for any x +/// - pow(1, y) = 1 for any y +/// - pow(x, 1) = x for any x +/// - pow(nan, y) = nan +/// - pow(x, nan) = nan +/// - pow(+-0, y) = +-inf for y an odd integer < 0 +/// - pow(+-0, -inf) = +inf +/// - pow(+-0, +inf) = +0 +/// - pow(+-0, y) = +inf for finite y < 0 and not an odd integer +/// - pow(+-0, y) = +-0 for y an odd integer > 0 +/// - pow(+-0, y) = +0 for finite y > 0 and not an odd integer +/// - pow(-1, +-inf) = 1 +/// - pow(x, +inf) = +inf for |x| > 1 +/// - pow(x, -inf) = +0 for |x| > 1 +/// - pow(x, +inf) = +0 for |x| < 1 +/// - pow(x, -inf) = +inf for |x| < 1 +/// - pow(+inf, y) = +inf for y > 0 +/// - pow(+inf, y) = +0 for y < 0 +/// - pow(-inf, y) = pow(-0, -y) +/// - pow(x, y) = nan for finite x < 0 and finite non-integer y pub fn pow(comptime T: type, x: T, y: T) T { if (@typeInfo(T) == builtin.TypeId.Int) { return math.powi(T, x, y) catch unreachable; @@ -53,15 +57,6 @@ pub fn pow(comptime T: type, x: T, y: T) T { return x; } - // special case sqrt - if (y == 0.5) { - return math.sqrt(x); - } - - if (y == -0.5) { - return 1 / math.sqrt(x); - } - if (x == 0) { if (y < 0) { // pow(+-0, y) = +- 0 for y an odd integer @@ -112,14 +107,16 @@ pub fn pow(comptime T: type, x: T, y: T) T { } } - var ay = y; - var flip = false; - if (ay < 0) { - ay = -ay; - flip = true; + // special case sqrt + if (y == 0.5) { + return math.sqrt(x); } - const r1 = math.modf(ay); + if (y == -0.5) { + return 1 / math.sqrt(x); + } + + const r1 = math.modf(math.fabs(y)); var yi = r1.ipart; var yf = r1.fpart; @@ -148,8 +145,18 @@ pub fn pow(comptime T: type, x: T, y: T) T { var xe = r2.exponent; var x1 = r2.significand; - var i = @floatToInt(i32, yi); + var i = @floatToInt(@IntType(true, T.bit_count), yi); while (i != 0) : (i >>= 1) { + const overflow_shift = math.floatExponentBits(T) + 1; + if (xe < -(1 << overflow_shift) or (1 << overflow_shift) < xe) { + // catch xe before it overflows the left shift below + // Since i != 0 it has at least one bit still set, so ae will accumulate xe + // on at least one more iteration, ae += xe is a lower bound on ae + // the lower bound on ae exceeds the size of a float exp + // so the final call to Ldexp will produce under/overflow (0/Inf) + ae += xe; + break; + } if (i & 1 == 1) { a1 *= x1; ae += xe; @@ -163,7 +170,7 @@ pub fn pow(comptime T: type, x: T, y: T) T { } // a *= a1 * 2^ae - if (flip) { + if (y < 0) { a1 = 1 / a1; ae = -ae; } @@ -202,6 +209,9 @@ test "math.pow.special" { expect(pow(f32, 45, 1.0) == 45); expect(pow(f32, -45, 1.0) == -45); expect(math.isNan(pow(f32, math.nan(f32), 5.0))); + expect(math.isPositiveInf(pow(f32, -math.inf(f32), 0.5))); + expect(math.isPositiveInf(pow(f32, -0, -0.5))); + expect(pow(f32, -0, 0.5) == 0); expect(math.isNan(pow(f32, 5.0, math.nan(f32)))); expect(math.isPositiveInf(pow(f32, 0.0, -1.0))); //expect(math.isNegativeInf(pow(f32, -0.0, -3.0))); TODO is this required? @@ -232,3 +242,11 @@ test "math.pow.special" { expect(math.isNan(pow(f32, -1.0, 1.2))); expect(math.isNan(pow(f32, -12.4, 78.5))); } + +test "math.pow.overflow" { + expect(math.isPositiveInf(pow(f64, 2, 1 << 32))); + expect(pow(f64, 2, -(1 << 32)) == 0); + expect(math.isNegativeInf(pow(f64, -2, (1 << 32) + 1))); + expect(pow(f64, 0.5, 1 << 45) == 0); + expect(math.isPositiveInf(pow(f64, 0.5, -(1 << 45)))); +} diff --git a/std/math/sin.zig b/std/math/sin.zig index e25b8a292b66a674df9e69c0842dcaf82980e65f..f21db4054ef3ba34d82100cab14a4205a5514ab5 100644 --- a/std/math/sin.zig +++ b/std/math/sin.zig @@ -1,19 +1,24 @@ -// Special Cases: +// Ported from go, which is licensed under a BSD-3 license. +// https://golang.org/LICENSE // -// - sin(+-0) = +-0 -// - sin(+-inf) = nan -// - sin(nan) = nan +// https://golang.org/src/math/sin.go const builtin = @import("builtin"); const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns the sine of the radian value x. +/// +/// Special Cases: +/// - sin(+-0) = +-0 +/// - sin(+-inf) = nan +/// - sin(nan) = nan pub fn sin(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { - f32 => sin32(x), - f64 => sin64(x), + f32 => sin_(T, x), + f64 => sin_(T, x), else => @compileError("sin not implemented for " ++ @typeName(T)), }; } @@ -34,83 +39,27 @@ const C3 = 2.48015872888517045348E-5; const C4 = -1.38888888888730564116E-3; const C5 = 4.16666666666665929218E-2; -// NOTE: This is taken from the go stdlib. The musl implementation is much more complex. -// -// This may have slight differences on some edge cases and may need to replaced if so. -fn sin32(x_: f32) f32 { - const pi4a = 7.85398125648498535156e-1; - const pi4b = 3.77489470793079817668E-8; - const pi4c = 2.69515142907905952645E-15; - const m4pi = 1.273239544735162542821171882678754627704620361328125; +const pi4a = 7.85398125648498535156e-1; +const pi4b = 3.77489470793079817668E-8; +const pi4c = 2.69515142907905952645E-15; +const m4pi = 1.273239544735162542821171882678754627704620361328125; - var x = x_; - if (x == 0 or math.isNan(x)) { - return x; - } - if (math.isInf(x)) { - return math.nan(f32); - } - - var sign = false; - if (x < 0) { - x = -x; - sign = true; - } - - var y = math.floor(x * m4pi); - var j = @floatToInt(i64, y); - - if (j & 1 == 1) { - j += 1; - y += 1; - } - - j &= 7; - if (j > 3) { - j -= 4; - sign = !sign; - } - - const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; - const w = z * z; - - const r = r: { - if (j == 1 or j == 2) { - break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))); - } else { - break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))); - } - }; - - if (sign) { - return -r; - } else { - return r; - } -} - -fn sin64(x_: f64) f64 { - const pi4a = 7.85398125648498535156e-1; - const pi4b = 3.77489470793079817668E-8; - const pi4c = 2.69515142907905952645E-15; - const m4pi = 1.273239544735162542821171882678754627704620361328125; +fn sin_(comptime T: type, x_: T) T { + const I = @IntType(true, T.bit_count); var x = x_; if (x == 0 or math.isNan(x)) { return x; } if (math.isInf(x)) { - return math.nan(f64); + return math.nan(T); } - var sign = false; - if (x < 0) { - x = -x; - sign = true; - } + var sign = x < 0; + x = math.fabs(x); var y = math.floor(x * m4pi); - var j = @floatToInt(i64, y); + var j = @floatToInt(I, y); if (j & 1 == 1) { j += 1; @@ -126,61 +75,56 @@ fn sin64(x_: f64) f64 { const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; const w = z * z; - const r = r: { - if (j == 1 or j == 2) { - break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))); - } else { - break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))); - } - }; + const r = if (j == 1 or j == 2) + 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))) + else + z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))); - if (sign) { - return -r; - } else { - return r; - } + return if (sign) -r else r; } test "math.sin" { - expect(sin(f32(0.0)) == sin32(0.0)); - expect(sin(f64(0.0)) == sin64(0.0)); + expect(sin(f32(0.0)) == sin_(f32, 0.0)); + expect(sin(f64(0.0)) == sin_(f64, 0.0)); expect(comptime (math.sin(f64(2))) == math.sin(f64(2))); } test "math.sin32" { const epsilon = 0.000001; - expect(math.approxEq(f32, sin32(0.0), 0.0, epsilon)); - expect(math.approxEq(f32, sin32(0.2), 0.198669, epsilon)); - expect(math.approxEq(f32, sin32(0.8923), 0.778517, epsilon)); - expect(math.approxEq(f32, sin32(1.5), 0.997495, epsilon)); - expect(math.approxEq(f32, sin32(37.45), -0.246544, epsilon)); - expect(math.approxEq(f32, sin32(89.123), 0.916166, epsilon)); + 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)); } test "math.sin64" { const epsilon = 0.000001; - expect(math.approxEq(f64, sin64(0.0), 0.0, epsilon)); - expect(math.approxEq(f64, sin64(0.2), 0.198669, epsilon)); - expect(math.approxEq(f64, sin64(0.8923), 0.778517, epsilon)); - expect(math.approxEq(f64, sin64(1.5), 0.997495, epsilon)); - expect(math.approxEq(f64, sin64(37.45), -0.246543, epsilon)); - expect(math.approxEq(f64, sin64(89.123), 0.916166, epsilon)); + 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)); } test "math.sin32.special" { - expect(sin32(0.0) == 0.0); - expect(sin32(-0.0) == -0.0); - expect(math.isNan(sin32(math.inf(f32)))); - expect(math.isNan(sin32(-math.inf(f32)))); - expect(math.isNan(sin32(math.nan(f32)))); + expect(sin_(f32, 0.0) == 0.0); + expect(sin_(f32, -0.0) == -0.0); + expect(math.isNan(sin_(f32, math.inf(f32)))); + expect(math.isNan(sin_(f32, -math.inf(f32)))); + expect(math.isNan(sin_(f32, math.nan(f32)))); } test "math.sin64.special" { - expect(sin64(0.0) == 0.0); - expect(sin64(-0.0) == -0.0); - expect(math.isNan(sin64(math.inf(f64)))); - expect(math.isNan(sin64(-math.inf(f64)))); - expect(math.isNan(sin64(math.nan(f64)))); + expect(sin_(f64, 0.0) == 0.0); + expect(sin_(f64, -0.0) == -0.0); + expect(math.isNan(sin_(f64, math.inf(f64)))); + expect(math.isNan(sin_(f64, -math.inf(f64)))); + expect(math.isNan(sin_(f64, math.nan(f64)))); } diff --git a/std/math/tan.zig b/std/math/tan.zig index fc11ebdef791443473f2122575e983b26747a667..e8259ee7ad2946fa70c96f29287e69ae013a941e 100644 --- a/std/math/tan.zig +++ b/std/math/tan.zig @@ -1,19 +1,24 @@ -// Special Cases: +// Ported from go, which is licensed under a BSD-3 license. +// https://golang.org/LICENSE // -// - tan(+-0) = +-0 -// - tan(+-inf) = nan -// - tan(nan) = nan +// https://golang.org/src/math/tan.go const builtin = @import("builtin"); const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns the tangent of the radian value x. +/// +/// Special Cases: +/// - tan(+-0) = +-0 +/// - tan(+-inf) = nan +/// - tan(nan) = nan pub fn tan(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { - f32 => tan32(x), - f64 => tan64(x), + f32 => tan_(f32, x), + f64 => tan_(f64, x), else => @compileError("tan not implemented for " ++ @typeName(T)), }; } @@ -27,80 +32,27 @@ const Tq2 = -1.32089234440210967447E6; const Tq3 = 2.50083801823357915839E7; const Tq4 = -5.38695755929454629881E7; -// NOTE: This is taken from the go stdlib. The musl implementation is much more complex. -// -// This may have slight differences on some edge cases and may need to replaced if so. -fn tan32(x_: f32) f32 { - const pi4a = 7.85398125648498535156e-1; - const pi4b = 3.77489470793079817668E-8; - const pi4c = 2.69515142907905952645E-15; - const m4pi = 1.273239544735162542821171882678754627704620361328125; +const pi4a = 7.85398125648498535156e-1; +const pi4b = 3.77489470793079817668E-8; +const pi4c = 2.69515142907905952645E-15; +const m4pi = 1.273239544735162542821171882678754627704620361328125; - var x = x_; - if (x == 0 or math.isNan(x)) { - return x; - } - if (math.isInf(x)) { - return math.nan(f32); - } - - var sign = false; - if (x < 0) { - x = -x; - sign = true; - } - - var y = math.floor(x * m4pi); - var j = @floatToInt(i64, y); - - if (j & 1 == 1) { - j += 1; - y += 1; - } - - const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; - const w = z * z; - - var r = r: { - if (w > 1e-14) { - break :r z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4)); - } else { - break :r z; - } - }; - - if (j & 2 == 2) { - r = -1 / r; - } - if (sign) { - r = -r; - } - - return r; -} - -fn tan64(x_: f64) f64 { - const pi4a = 7.85398125648498535156e-1; - const pi4b = 3.77489470793079817668E-8; - const pi4c = 2.69515142907905952645E-15; - const m4pi = 1.273239544735162542821171882678754627704620361328125; +fn tan_(comptime T: type, x_: T) T { + const I = @IntType(true, T.bit_count); var x = x_; if (x == 0 or math.isNan(x)) { return x; } if (math.isInf(x)) { - return math.nan(f64); + return math.nan(T); } - var sign = false; - if (x < 0) { - x = -x; - sign = true; - } + var sign = x < 0; + x = math.fabs(x); var y = math.floor(x * m4pi); - var j = @floatToInt(i64, y); + var j = @floatToInt(I, y); if (j & 1 == 1) { j += 1; @@ -110,63 +62,57 @@ fn tan64(x_: f64) f64 { const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; const w = z * z; - var r = r: { - if (w > 1e-14) { - break :r z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4)); - } else { - break :r z; - } - }; + var r = if (w > 1e-14) + z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4)) + else + z; if (j & 2 == 2) { r = -1 / r; } - if (sign) { - r = -r; - } - return r; + return if (sign) -r else r; } test "math.tan" { - expect(tan(f32(0.0)) == tan32(0.0)); - expect(tan(f64(0.0)) == tan64(0.0)); + expect(tan(f32(0.0)) == tan_(f32, 0.0)); + expect(tan(f64(0.0)) == tan_(f64, 0.0)); } test "math.tan32" { const epsilon = 0.000001; - expect(math.approxEq(f32, tan32(0.0), 0.0, epsilon)); - expect(math.approxEq(f32, tan32(0.2), 0.202710, epsilon)); - expect(math.approxEq(f32, tan32(0.8923), 1.240422, epsilon)); - expect(math.approxEq(f32, tan32(1.5), 14.101420, epsilon)); - expect(math.approxEq(f32, tan32(37.45), -0.254397, epsilon)); - expect(math.approxEq(f32, tan32(89.123), 2.285852, epsilon)); + 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)); } test "math.tan64" { const epsilon = 0.000001; - expect(math.approxEq(f64, tan64(0.0), 0.0, epsilon)); - expect(math.approxEq(f64, tan64(0.2), 0.202710, epsilon)); - expect(math.approxEq(f64, tan64(0.8923), 1.240422, epsilon)); - expect(math.approxEq(f64, tan64(1.5), 14.101420, epsilon)); - expect(math.approxEq(f64, tan64(37.45), -0.254397, epsilon)); - expect(math.approxEq(f64, tan64(89.123), 2.2858376, epsilon)); + 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)); } test "math.tan32.special" { - expect(tan32(0.0) == 0.0); - expect(tan32(-0.0) == -0.0); - expect(math.isNan(tan32(math.inf(f32)))); - expect(math.isNan(tan32(-math.inf(f32)))); - expect(math.isNan(tan32(math.nan(f32)))); + expect(tan_(f32, 0.0) == 0.0); + expect(tan_(f32, -0.0) == -0.0); + expect(math.isNan(tan_(f32, math.inf(f32)))); + expect(math.isNan(tan_(f32, -math.inf(f32)))); + expect(math.isNan(tan_(f32, math.nan(f32)))); } test "math.tan64.special" { - expect(tan64(0.0) == 0.0); - expect(tan64(-0.0) == -0.0); - expect(math.isNan(tan64(math.inf(f64)))); - expect(math.isNan(tan64(-math.inf(f64)))); - expect(math.isNan(tan64(math.nan(f64)))); + expect(tan_(f64, 0.0) == 0.0); + expect(tan_(f64, -0.0) == -0.0); + expect(math.isNan(tan_(f64, math.inf(f64)))); + expect(math.isNan(tan_(f64, -math.inf(f64)))); + expect(math.isNan(tan_(f64, math.nan(f64)))); } -- 2.54.0 From 89d71a960b0f90335fded84a449a84806bb6f661 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Wed, 1 May 2019 18:15:57 +1200 Subject: [PATCH 2/3] std.math: Add documentation for all functions and algorithm sources --- std/math/acos.zig | 10 ++- std/math/acosh.zig | 12 ++- std/math/asin.zig | 12 ++- std/math/asinh.zig | 14 +++- std/math/atan.zig | 12 ++- std/math/atan2.zig | 42 +++++----- std/math/atanh.zig | 14 +++- std/math/big/int.zig | 157 ++++++++++++++++++++++++++----------- std/math/big/rational.zig | 68 +++++++++++++--- std/math/cbrt.zig | 14 +++- std/math/ceil.zig | 14 +++- std/math/complex.zig | 12 +++ std/math/complex/abs.zig | 1 + std/math/complex/acos.zig | 1 + std/math/complex/acosh.zig | 1 + std/math/complex/arg.zig | 1 + std/math/complex/asin.zig | 1 + std/math/complex/asinh.zig | 1 + std/math/complex/atan.zig | 7 ++ std/math/complex/atanh.zig | 1 + std/math/complex/conj.zig | 1 + std/math/complex/cos.zig | 1 + std/math/complex/cosh.zig | 7 ++ std/math/complex/exp.zig | 7 ++ std/math/complex/ldexp.zig | 7 ++ std/math/complex/log.zig | 1 + std/math/complex/pow.zig | 1 + std/math/complex/proj.zig | 1 + std/math/complex/sin.zig | 1 + std/math/complex/sinh.zig | 7 ++ std/math/complex/sqrt.zig | 8 ++ std/math/complex/tan.zig | 1 + std/math/complex/tanh.zig | 7 ++ std/math/copysign.zig | 7 ++ std/math/cosh.zig | 14 +++- std/math/exp.zig | 12 ++- std/math/exp2.zig | 12 ++- std/math/expm1.zig | 17 +++- std/math/expo2.zig | 7 ++ std/math/fabs.zig | 12 ++- std/math/floor.zig | 14 +++- std/math/frexp.zig | 15 +++- std/math/hypot.zig | 16 ++-- std/math/ilogb.zig | 14 +++- std/math/inf.zig | 1 + std/math/isfinite.zig | 1 + std/math/isinf.zig | 3 + std/math/isnan.zig | 6 +- std/math/isnormal.zig | 1 + std/math/ln.zig | 16 ++-- std/math/log.zig | 7 ++ std/math/log10.zig | 16 ++-- std/math/log1p.zig | 18 +++-- std/math/log2.zig | 16 ++-- std/math/modf.zig | 13 ++- std/math/nan.zig | 6 +- std/math/powi.zig | 22 +++--- std/math/round.zig | 14 +++- std/math/scalbn.zig | 7 ++ std/math/signbit.zig | 1 + std/math/sinh.zig | 14 +++- std/math/sqrt.zig | 14 ++-- std/math/tanh.zig | 14 +++- std/math/trunc.zig | 14 +++- 64 files changed, 592 insertions(+), 197 deletions(-) diff --git a/std/math/acos.zig b/std/math/acos.zig index 763d9d8abde256bf4fe361cc305e519905aca952..de07da8fe0cd292c6272c505d62faeb1650902d1 100644 --- a/std/math/acos.zig +++ b/std/math/acos.zig @@ -1,11 +1,17 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - acos(x) = nan if x < -1 or x > 1 +// https://git.musl-libc.org/cgit/musl/tree/src/math/acosf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/acos.c const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns the arc-cosine of x. +/// +/// Special cases: +/// - acos(x) = nan if x < -1 or x > 1 pub fn acos(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/acosh.zig b/std/math/acosh.zig index a2a99268635d6972e279f85b883d8de5e812f137..503c0433fc35fac0a5cea135972eac22a666faf0 100644 --- a/std/math/acosh.zig +++ b/std/math/acosh.zig @@ -1,13 +1,19 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - acosh(x) = snan if x < 1 -// - acosh(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/acoshf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/acosh.c const builtin = @import("builtin"); const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns the hyperbolic arc-cosine of x. +/// +/// Special cases: +/// - acosh(x) = snan if x < 1 +/// - acosh(nan) = nan pub fn acosh(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/asin.zig b/std/math/asin.zig index b01a1ac49ea067aae9154df67a2bd9a9a656fe98..2db9f86ff1051a9ed293c3cff942ead872111c3b 100644 --- a/std/math/asin.zig +++ b/std/math/asin.zig @@ -1,12 +1,18 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - asin(+-0) = +-0 -// - asin(x) = nan if x < -1 or x > 1 +// https://git.musl-libc.org/cgit/musl/tree/src/math/asinf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/asin.c const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns the arc-sin of x. +/// +/// Special Cases: +/// - asin(+-0) = +-0 +/// - asin(x) = nan if x < -1 or x > 1 pub fn asin(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/asinh.zig b/std/math/asinh.zig index cef9bfb23f520cdcbcedccbdfe22bc26168bfed4..0fb51d1b4395ef0c545a64717f79e97aea501e70 100644 --- a/std/math/asinh.zig +++ b/std/math/asinh.zig @@ -1,14 +1,20 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - asinh(+-0) = +-0 -// - asinh(+-inf) = +-inf -// - asinh(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/asinhf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/asinh.c const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; +/// Returns the hyperbolic arc-sin of x. +/// +/// Special Cases: +/// - asinh(+-0) = +-0 +/// - asinh(+-inf) = +-inf +/// - asinh(nan) = nan pub fn asinh(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/atan.zig b/std/math/atan.zig index ba5a11dd10dc794a4164f686135084c703fd778a..cc4cad0dd4014cd2a2e75b67ef1c77e6ae3d55ed 100644 --- a/std/math/atan.zig +++ b/std/math/atan.zig @@ -1,12 +1,18 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - atan(+-0) = +-0 -// - atan(+-inf) = +-pi/2 +// https://git.musl-libc.org/cgit/musl/tree/src/math/atanf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/atan.c const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns the arc-tangent of x. +/// +/// Special Cases: +/// - atan(+-0) = +-0 +/// - atan(+-inf) = +-pi/2 pub fn atan(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/atan2.zig b/std/math/atan2.zig index 7f135074021197bd6a00848320bc8b5d2536394d..68e381607dd341fa5eec537aa2734aec97493ece 100644 --- a/std/math/atan2.zig +++ b/std/math/atan2.zig @@ -1,27 +1,33 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// atan2(y, nan) = nan -// atan2(nan, x) = nan -// atan2(+0, x>=0) = +0 -// atan2(-0, x>=0) = -0 -// atan2(+0, x<=-0) = +pi -// atan2(-0, x<=-0) = -pi -// atan2(y>0, 0) = +pi/2 -// atan2(y<0, 0) = -pi/2 -// atan2(+inf, +inf) = +pi/4 -// atan2(-inf, +inf) = -pi/4 -// atan2(+inf, -inf) = 3pi/4 -// atan2(-inf, -inf) = -3pi/4 -// atan2(y, +inf) = 0 -// atan2(y>0, -inf) = +pi -// atan2(y<0, -inf) = -pi -// atan2(+inf, x) = +pi/2 -// atan2(-inf, x) = -pi/2 +// https://git.musl-libc.org/cgit/musl/tree/src/math/atan2f.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/atan2.c const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns the arc-tangent of y/x. +/// +/// Special Cases: +/// - atan2(y, nan) = nan +/// - atan2(nan, x) = nan +/// - atan2(+0, x>=0) = +0 +/// - atan2(-0, x>=0) = -0 +/// - atan2(+0, x<=-0) = +pi +/// - atan2(-0, x<=-0) = -pi +/// - atan2(y>0, 0) = +pi/2 +/// - atan2(y<0, 0) = -pi/2 +/// - atan2(+inf, +inf) = +pi/4 +/// - atan2(-inf, +inf) = -pi/4 +/// - atan2(+inf, -inf) = 3pi/4 +/// - atan2(-inf, -inf) = -3pi/4 +/// - atan2(y, +inf) = 0 +/// - atan2(y>0, -inf) = +pi +/// - atan2(y<0, -inf) = -pi +/// - atan2(+inf, x) = +pi/2 +/// - atan2(-inf, x) = -pi/2 pub fn atan2(comptime T: type, y: T, x: T) T { return switch (T) { f32 => atan2_32(y, x), diff --git a/std/math/atanh.zig b/std/math/atanh.zig index 9056064f5a48c8cc23479b4db159aafe8450b46e..8ba29be761aefe917ea8225b18effcc5157d5fec 100644 --- a/std/math/atanh.zig +++ b/std/math/atanh.zig @@ -1,14 +1,20 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - atanh(+-1) = +-inf with signal -// - atanh(x) = nan if |x| > 1 with signal -// - atanh(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/atanhf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/atanh.c const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; +/// Returns the hyperbolic arc-tangent of x. +/// +/// Special Cases: +/// - atanh(+-1) = +-inf with signal +/// - atanh(x) = nan if |x| > 1 with signal +/// - atanh(nan) = nan pub fn atanh(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/big/int.zig b/std/math/big/int.zig index aced892e186bafbb486fa129d31a8e03f9bd16e0..beac3c85fee3432d3509da00a4c15b1411a112a6 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -21,32 +21,49 @@ comptime { debug.assert(Limb.is_signed == false); } +/// An arbitrary-precision big integer. +/// +/// Memory is allocated by an Int as needed to ensure operations never overflow. The range of an +/// Int is bounded only by available memory. pub const Int = struct { const sign_bit: usize = 1 << (usize.bit_count - 1); + /// Default number of limbs to allocate on creation of an Int. + pub const default_capacity = 4; + + /// Allocator used by the Int when requesting memory. allocator: ?*Allocator, - // - little-endian ordered - // - len >= 1 always - // - zero value -> len == 1 with limbs[0] == 0 + + /// Raw digits. These are: + /// + /// * Little-endian ordered + /// * limbs.len >= 1 + /// * Zero is represent as Int.len() == 1 with limbs[0] == 0. + /// + /// Accessing limbs directly should be avoided. limbs: []Limb, - // High bit is the sign bit. 1 is negative, 0 positive. - // Remaining bits indicate the number of used limbs. - // - // If Zig gets smarter about packing data, this can be rewritten as a u1 and usize - 1 field. + + /// High bit is the sign bit. If set, Int is negative, else Int is positive. + /// The remaining bits represent the number of limbs used by Int. metadata: usize, - const default_capacity = 4; - + /// Creates a new Int. default_capacity limbs will be allocated immediately. + /// Int will be zeroed. pub fn init(allocator: *Allocator) !Int { return try Int.initCapacity(allocator, default_capacity); } + /// Creates a new Int. Int will be set to `value`. + /// + /// This is identical to an `init`, followed by a `set`. pub fn initSet(allocator: *Allocator, value: var) !Int { var s = try Int.init(allocator); try s.set(value); return s; } + /// Creates a new Int with a specific capacity. If capacity < default_capacity then the + /// default capacity will be used instead. pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int { return Int{ .allocator = allocator, @@ -59,14 +76,17 @@ pub const Int = struct { }; } + /// Returns the number of limbs currently in use. pub fn len(self: Int) usize { return self.metadata & ~sign_bit; } + /// Returns whether an Int is positive. pub fn isPositive(self: Int) bool { return self.metadata & sign_bit == 0; } + /// Sets the sign of an Int. pub fn setSign(self: *Int, positive: bool) void { if (positive) { self.metadata &= ~sign_bit; @@ -75,14 +95,17 @@ pub const Int = struct { } } + /// Sets the length of an Int. + /// + /// If setLen is used, then the Int must be normalized to suit. pub fn setLen(self: *Int, new_len: usize) void { self.metadata &= sign_bit; self.metadata |= new_len; } - // Initialize an Int directly from a fixed set of limb values. This is considered read-only - // and cannot be used as a receiver argument to any functions. If this tries to allocate - // at any point a panic will occur due to the null allocator. + /// Returns an Int backed by a fixed set of limb values. + /// This is read-only and cannot be used as a result argument. If the Int tries to allocate + /// memory a runtime panic will occur. pub fn initFixed(limbs: []const Limb) Int { var self = Int{ .allocator = null, @@ -95,6 +118,9 @@ pub const Int = struct { return self; } + /// Ensures an Int has enough space allocated for capacity limbs. If the Int does not have + /// sufficient capacity, the exact amount will be allocated. This occurs even if the requested + /// capacity is only greater than the current capacity by one limb. pub fn ensureCapacity(self: *Int, capacity: usize) !void { self.assertWritable(); if (capacity <= self.limbs.len) { @@ -110,12 +136,15 @@ pub const Int = struct { } } + /// Frees all memory associated with an Int. pub fn deinit(self: *Int) void { self.assertWritable(); self.allocator.?.free(self.limbs); self.* = undefined; } + /// Clones an Int and returns a new Int with the same value. The new Int is a deep copy and + /// can be modified separately from the original. pub fn clone(other: Int) !Int { other.assertWritable(); return Int{ @@ -129,6 +158,8 @@ pub const Int = struct { }; } + /// Copies the value of an Int to an existing Int so that they both have the same value. + /// Extra memory will be allocated if the receiver does not have enough capacity. pub fn copy(self: *Int, other: Int) !void { self.assertWritable(); if (self.limbs.ptr == other.limbs.ptr) { @@ -140,6 +171,8 @@ pub const Int = struct { self.metadata = other.metadata; } + /// Efficiently swap an Int with another. This swaps the limb pointers and a full copy is not + /// performed. The address of the limbs field will not be the same after this function. pub fn swap(self: *Int, other: *Int) void { self.assertWritable(); mem.swap(Int, self, other); @@ -152,35 +185,39 @@ pub const Int = struct { debug.warn("\n"); } + /// Negate the sign of an Int. pub fn negate(self: *Int) void { self.metadata ^= sign_bit; } + /// Make an Int positive. pub fn abs(self: *Int) void { self.metadata &= ~sign_bit; } + /// Returns true if an Int is odd. pub fn isOdd(self: Int) bool { return self.limbs[0] & 1 != 0; } + /// Returns true if an Int is even. pub fn isEven(self: Int) bool { return !self.isOdd(); } - // Returns the number of bits required to represent the absolute value of self. + /// Returns the number of bits required to represent the absolute value an Int. fn bitCountAbs(self: Int) usize { return (self.len() - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len() - 1])); } - // Returns the number of bits required to represent the integer in twos-complement form. - // - // If the integer is negative the value returned is the number of bits needed by a signed - // integer to represent the value. If positive the value is the number of bits for an - // unsigned integer. Any unsigned integer will fit in the signed integer with bitcount - // one greater than the returned value. - // - // e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7. + /// Returns the number of bits required to represent the integer in twos-complement form. + /// + /// If the integer is negative the value returned is the number of bits needed by a signed + /// integer to represent the value. If positive the value is the number of bits for an + /// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount + /// one greater than the returned value. + /// + /// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7. fn bitCountTwosComp(self: Int) usize { var bits = self.bitCountAbs(); @@ -203,7 +240,7 @@ pub const Int = struct { return bits; } - pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool { + fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool { if (self.eqZero()) { return true; } @@ -215,18 +252,20 @@ pub const Int = struct { return bit_count >= req_bits; } + /// Returns whether self can fit into an integer of the requested type. pub fn fits(self: Int, comptime T: type) bool { return self.fitsInTwosComp(T.is_signed, T.bit_count); } - // Returns the approximate size of the integer in the given base. Negative values accommodate for - // the minus sign. This is used for determining the number of characters needed to print the - // value. It is inexact and will exceed the given value by 1-2 digits. + /// Returns the approximate size of the integer in the given base. Negative values accommodate for + /// the minus sign. This is used for determining the number of characters needed to print the + /// value. It is inexact and may exceed the given value by ~1-2 bytes. pub fn sizeInBase(self: Int, base: usize) usize { const bit_count = usize(@boolToInt(!self.isPositive())) + self.bitCountAbs(); return (bit_count / math.log2(base)) + 1; } + /// Sets an Int to value. Value must be an primitive integer type. pub fn set(self: *Int, value: var) Allocator.Error!void { self.assertWritable(); const T = @typeOf(value); @@ -290,6 +329,9 @@ pub const Int = struct { TargetTooSmall, }; + /// Convert self to type T. + /// + /// Returns an error if self cannot be narrowed into the requested type without truncation. pub fn to(self: Int, comptime T: type) ConvertError!T { switch (@typeId(T)) { TypeId.Int => { @@ -353,6 +395,13 @@ pub const Int = struct { }; } + /// Set self from the string representation `value`. + /// + /// value must contain only digits <= `base`. Base prefixes are not allowed (e.g. 0x43 should + /// simply be 43). + /// + /// Returns an error if memory could not be allocated or `value` has invalid digits for the + /// requested base. pub fn setString(self: *Int, base: u8, value: []const u8) !void { self.assertWritable(); if (base < 2 or base > 16) { @@ -380,6 +429,8 @@ pub const Int = struct { self.setSign(positive); } + /// Converts self to a string in the requested base. Memory is allocated from the provided + /// allocator and not the one present in self. /// TODO make this call format instead of the other way around pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 { if (base < 2 or base > 16) { @@ -463,7 +514,7 @@ pub const Int = struct { return s; } - /// for the std lib format function + /// To allow `std.fmt.printf` to work with Int. /// TODO make this non-allocating pub fn format( self: Int, @@ -480,7 +531,7 @@ pub const Int = struct { return output(context, str); } - // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively. + /// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively. pub fn cmpAbs(a: Int, b: Int) i8 { if (a.len() < b.len()) { return -1; @@ -505,7 +556,7 @@ pub const Int = struct { } } - // returns -1, 0, 1 if a < b, a == b or a > b respectively. + /// Returns -1, 0, 1 if a < b, a == b or a > b respectively. pub fn cmp(a: Int, b: Int) i8 { if (a.isPositive() != b.isPositive()) { return if (a.isPositive()) i8(1) else -1; @@ -515,17 +566,17 @@ pub const Int = struct { } } - // if a == 0 + /// Returns true if a == 0. pub fn eqZero(a: Int) bool { return a.len() == 1 and a.limbs[0] == 0; } - // if |a| == |b| + /// Returns true if |a| == |b|. pub fn eqAbs(a: Int, b: Int) bool { return cmpAbs(a, b) == 0; } - // if a == b + /// Returns true if a == b. pub fn eq(a: Int, b: Int) bool { return cmp(a, b) == 0; } @@ -559,7 +610,11 @@ pub const Int = struct { }; } - // r = a + b + /// r = a + b + /// + /// r, a and b may be aliases. + /// + /// Returns an error if memory could not be allocated. pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void { r.assertWritable(); if (a.eqZero()) { @@ -617,7 +672,11 @@ pub const Int = struct { r[i] = carry; } - // r = a - b + /// r = a - b + /// + /// r, a and b may be aliases. + /// + /// Returns an error if memory could not be allocated. pub fn sub(r: *Int, a: Int, b: Int) !void { r.assertWritable(); if (a.isPositive() != b.isPositive()) { @@ -684,9 +743,11 @@ pub const Int = struct { debug.assert(borrow == 0); } - // rma = a * b - // - // For greatest efficiency, ensure rma does not alias a or b. + /// rma = a * b + /// + /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. + /// + /// Returns an error if memory could not be allocated. pub fn mul(rma: *Int, a: Int, b: Int) !void { rma.assertWritable(); @@ -759,6 +820,9 @@ pub const Int = struct { } } + /// q = a / b (rem r) + /// + /// a / b are floored (rounded towards 0). pub fn divFloor(q: *Int, r: *Int, a: Int, b: Int) !void { try div(q, r, a, b); @@ -771,6 +835,9 @@ pub const Int = struct { r.setSign(b.isPositive()); } + /// q = a / b (rem r) + /// + /// a / b are truncated (rounded towards -inf). pub fn divTrunc(q: *Int, r: *Int, a: Int, b: Int) !void { try div(q, r, a, b); r.setSign(a.isPositive()); @@ -969,7 +1036,7 @@ pub const Int = struct { r.normalize(r.len()); } - // r = a << shift, in other words, r = a * 2^shift + /// r = a << shift, in other words, r = a * 2^shift pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void { r.assertWritable(); @@ -1002,7 +1069,7 @@ pub const Int = struct { mem.set(Limb, r[0 .. limb_shift - 1], 0); } - // r = a >> shift + /// r = a >> shift pub fn shiftRight(r: *Int, a: Int, shift: usize) !void { r.assertWritable(); @@ -1038,7 +1105,9 @@ pub const Int = struct { } } - // r = a | b + /// r = a | b + /// + /// a and b are zero-extended to the longer of a or b. pub fn bitOr(r: *Int, a: Int, b: Int) !void { r.assertWritable(); @@ -1067,7 +1136,7 @@ pub const Int = struct { } } - // r = a & b + /// r = a & b pub fn bitAnd(r: *Int, a: Int, b: Int) !void { r.assertWritable(); @@ -1093,7 +1162,7 @@ pub const Int = struct { } } - // r = a ^ b + /// r = a ^ b pub fn bitXor(r: *Int, a: Int, b: Int) !void { r.assertWritable(); @@ -1279,10 +1348,8 @@ test "big.int bitcount/to" { try a.set(0); testing.expect(a.bitCountTwosComp() == 0); - // TODO: stack smashing - // testing.expect((try a.to(u0)) == 0); - // TODO: sigsegv - // testing.expect((try a.to(i0)) == 0); + testing.expect((try a.to(u0)) == 0); + testing.expect((try a.to(i0)) == 0); try a.set(-1); testing.expect(a.bitCountTwosComp() == 1); diff --git a/std/math/big/rational.zig b/std/math/big/rational.zig index 34cf3bf7644b06b16c44fac155f2cbfb0bd9150a..58a5e3ac76fd2c2dd690bdd5ac048fbbd384cf8f 100644 --- a/std/math/big/rational.zig +++ b/std/math/big/rational.zig @@ -14,11 +14,22 @@ const Limb = bn.Limb; const DoubleLimb = bn.DoubleLimb; const Int = bn.Int; +/// An arbitrary-precision rational number. +/// +/// Memory is allocated as needed for operations to ensure full precision is kept. The precision +/// of a Rational is only bounded by memory. +/// +/// Rational's are always normalized. That is, for a Rational r = p/q where p and q are integers, +/// gcd(p, q) = 1 always. pub const Rational = struct { - // Sign of Rational is sign of p. Sign of q is ignored + /// Numerator. Determines the sign of the Rational. p: Int, + + /// Denominator. Sign is ignored. q: Int, + /// Create a new Rational. A small amount of memory will be allocated on initialization. + /// This will be 2 * Int.default_capacity. pub fn init(a: *Allocator) !Rational { return Rational{ .p = try Int.init(a), @@ -26,18 +37,21 @@ pub const Rational = struct { }; } + /// Frees all memory associated with a Rational. pub fn deinit(self: *Rational) void { self.p.deinit(); self.q.deinit(); } + /// Set a Rational from a primitive integer type. pub fn setInt(self: *Rational, a: var) !void { try self.p.set(a); try self.q.set(1); } - // TODO: Accept a/b fractions and exponent form + /// Set a Rational from a string of the form `A/B` where A and B are base-10 integers. pub fn setFloatString(self: *Rational, str: []const u8) !void { + // TODO: Accept a/b fractions and exponent form if (str.len == 0) { return error.InvalidFloatString; } @@ -111,8 +125,10 @@ pub const Rational = struct { } } - // Translated from golang.go/src/math/big/rat.go. + /// Set a Rational from a floating-point value. The rational will have enough precision to + /// completely represent the provided float. pub fn setFloat(self: *Rational, comptime T: type, f: T) !void { + // Translated from golang.go/src/math/big/rat.go. debug.assert(@typeId(T) == builtin.TypeId.Float); const UnsignedIntType = @IntType(false, T.bit_count); @@ -164,8 +180,13 @@ pub const Rational = struct { try self.reduce(); } - // Translated from golang.go/src/math/big/rat.go. + /// Return a floating-point value that is the closest value to a Rational. + /// + /// The result may not be exact if the Rational is too precise or too large for the + /// target type. pub fn toFloat(self: Rational, comptime T: type) !T { + // Translated from golang.go/src/math/big/rat.go. + // TODO: Indicate whether the result is not exact. debug.assert(@typeId(T) == builtin.TypeId.Float); const fsize = T.bit_count; @@ -259,6 +280,7 @@ pub const Rational = struct { return if (self.p.isPositive()) f else -f; } + /// Set a rational from an integer ratio. pub fn setRatio(self: *Rational, p: var, q: var) !void { try self.p.set(p); try self.q.set(q); @@ -273,11 +295,13 @@ pub const Rational = struct { } } + /// Set a Rational directly from an Int. pub fn copyInt(self: *Rational, a: Int) !void { try self.p.copy(a); try self.q.set(1); } + /// Set a Rational directly from a ratio of two Int's. pub fn copyRatio(self: *Rational, a: Int, b: Int) !void { try self.p.copy(a); try self.q.copy(b); @@ -288,23 +312,29 @@ pub const Rational = struct { try self.reduce(); } + /// Make a Rational positive. pub fn abs(r: *Rational) void { r.p.abs(); } + /// Negate the sign of a Rational. pub fn negate(r: *Rational) void { r.p.negate(); } + /// Efficiently swap a Rational with another. This swaps the limb pointers and a full copy is not + /// performed. The address of the limbs field will not be the same after this function. pub fn swap(r: *Rational, other: *Rational) void { r.p.swap(&other.p); r.q.swap(&other.q); } + /// Returns -1, 0, 1 if a < b, a == b or a > b respectively. pub fn cmp(a: Rational, b: Rational) !i8 { return cmpInternal(a, b, true); } + /// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively. pub fn cmpAbs(a: Rational, b: Rational) !i8 { return cmpInternal(a, b, false); } @@ -325,9 +355,11 @@ pub const Rational = struct { return if (is_abs) q.cmpAbs(p) else q.cmp(p); } - // r/q = ap/aq + bp/bq = (ap*bq + bp*aq) / (aq*bq) - // - // For best performance, rma should not alias a or b. + /// rma = a + b. + /// + /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. + /// + /// Returns an error if memory could not be allocated. pub fn add(rma: *Rational, a: Rational, b: Rational) !void { var r = rma; var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr; @@ -351,9 +383,11 @@ pub const Rational = struct { try r.reduce(); } - // r/q = ap/aq - bp/bq = (ap*bq - bp*aq) / (aq*bq) - // - // For best performance, rma should not alias a or b. + /// rma = a - b. + /// + /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. + /// + /// Returns an error if memory could not be allocated. pub fn sub(rma: *Rational, a: Rational, b: Rational) !void { var r = rma; var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr; @@ -377,14 +411,22 @@ pub const Rational = struct { try r.reduce(); } - // r/q = ap/aq * bp/bq = ap*bp / aq*bq + /// rma = a * b. + /// + /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. + /// + /// Returns an error if memory could not be allocated. pub fn mul(r: *Rational, a: Rational, b: Rational) !void { try r.p.mul(a.p, b.p); try r.q.mul(a.q, b.q); try r.reduce(); } - // r/q = (ap/aq) / (bp/bq) = ap*bq / bp*aq + /// rma = a / b. + /// + /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. + /// + /// Returns an error if memory could not be allocated. pub fn div(r: *Rational, a: Rational, b: Rational) !void { if (b.p.eqZero()) { @panic("division by zero"); @@ -395,7 +437,7 @@ pub const Rational = struct { try r.reduce(); } - // r/q = q/r + /// Invert the numerator and denominator fields of a Rational. p/q => q/p. pub fn invert(r: *Rational) void { Int.swap(&r.p, &r.q); } diff --git a/std/math/cbrt.zig b/std/math/cbrt.zig index 94972552697a80858f1be15218995b1002dfbb99..5241e31323ea4e3b14e9d55e9392897b5dafffc4 100644 --- a/std/math/cbrt.zig +++ b/std/math/cbrt.zig @@ -1,13 +1,19 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - cbrt(+-0) = +-0 -// - cbrt(+-inf) = +-inf -// - cbrt(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/cbrtf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/cbrt.c const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns the cube root of x. +/// +/// Special Cases: +/// - cbrt(+-0) = +-0 +/// - cbrt(+-inf) = +-inf +/// - cbrt(nan) = nan pub fn cbrt(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/ceil.zig b/std/math/ceil.zig index da83e0ec59ed737bfb4bb41e91e6a77055c66ac5..5f86093a6d6704f3a14549cba6800d4be55f4c41 100644 --- a/std/math/ceil.zig +++ b/std/math/ceil.zig @@ -1,14 +1,20 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - ceil(+-0) = +-0 -// - ceil(+-inf) = +-inf -// - ceil(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c const builtin = @import("builtin"); const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns the least integer value greater than of equal to x. +/// +/// Special Cases: +/// - ceil(+-0) = +-0 +/// - ceil(+-inf) = +-inf +/// - ceil(nan) = nan pub fn ceil(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/complex.zig b/std/math/complex.zig index cc0573b2271f7850a9dcaead084c329556050b28..e5574f9ceed4dfbe14e74f05c2b4a3d04d5abf9b 100644 --- a/std/math/complex.zig +++ b/std/math/complex.zig @@ -23,13 +23,18 @@ pub const sqrt = @import("complex/sqrt.zig").sqrt; pub const tanh = @import("complex/tanh.zig").tanh; pub const tan = @import("complex/tan.zig").tan; +/// A complex number consisting of a real an imaginary part. T must be a floating-point value. pub fn Complex(comptime T: type) type { return struct { const Self = @This(); + /// Real part. re: T, + + /// Imaginary part. im: T, + /// Create a new Complex number from the given real and imaginary parts. pub fn new(re: T, im: T) Self { return Self{ .re = re, @@ -37,6 +42,7 @@ pub fn Complex(comptime T: type) type { }; } + /// Returns the sum of two complex numbers. pub fn add(self: Self, other: Self) Self { return Self{ .re = self.re + other.re, @@ -44,6 +50,7 @@ pub fn Complex(comptime T: type) type { }; } + /// Returns the subtraction of two complex numbers. pub fn sub(self: Self, other: Self) Self { return Self{ .re = self.re - other.re, @@ -51,6 +58,7 @@ pub fn Complex(comptime T: type) type { }; } + /// Returns the product of two complex numbers. pub fn mul(self: Self, other: Self) Self { return Self{ .re = self.re * other.re - self.im * other.im, @@ -58,6 +66,7 @@ pub fn Complex(comptime T: type) type { }; } + /// Returns the quotient of two complex numbers. pub fn div(self: Self, other: Self) Self { const re_num = self.re * other.re + self.im * other.im; const im_num = self.im * other.re - self.re * other.im; @@ -69,6 +78,7 @@ pub fn Complex(comptime T: type) type { }; } + /// Returns the complex conjugate of a number. pub fn conjugate(self: Self) Self { return Self{ .re = self.re, @@ -76,6 +86,7 @@ pub fn Complex(comptime T: type) type { }; } + /// Returns the reciprocal of a complex number. pub fn reciprocal(self: Self) Self { const m = self.re * self.re + self.im * self.im; return Self{ @@ -84,6 +95,7 @@ pub fn Complex(comptime T: type) type { }; } + /// Returns the magnitude of a complex number. pub fn magnitude(self: Self) T { return math.sqrt(self.re * self.re + self.im * self.im); } diff --git a/std/math/complex/abs.zig b/std/math/complex/abs.zig index e1368d6ef64414793d4639fe5d8bfff9fab30e98..8105f57218794142d69ec3b7ba1add6896af0205 100644 --- a/std/math/complex/abs.zig +++ b/std/math/complex/abs.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the absolute value (modulus) of z. pub fn abs(z: var) @typeOf(z.re) { const T = @typeOf(z.re); return math.hypot(T, z.re, z.im); diff --git a/std/math/complex/acos.zig b/std/math/complex/acos.zig index 8aed26a71b5e3ed3be3d42cab1679ce60ee2d750..f3526cc9fff5c9615846151805ddf04357953cf9 100644 --- a/std/math/complex/acos.zig +++ b/std/math/complex/acos.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the arc-cosine of z. pub fn acos(z: var) Complex(@typeOf(z.re)) { const T = @typeOf(z.re); const q = cmath.asin(z); diff --git a/std/math/complex/acosh.zig b/std/math/complex/acosh.zig index e72bf431fe528a3f89fe1c6abbf8824de32e798f..6f0fd2e36cd88f03318bdc7dd058fe79c7c046c7 100644 --- a/std/math/complex/acosh.zig +++ b/std/math/complex/acosh.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the hyperbolic arc-cosine of z. pub fn acosh(z: var) Complex(@typeOf(z.re)) { const T = @typeOf(z.re); const q = cmath.acos(z); diff --git a/std/math/complex/arg.zig b/std/math/complex/arg.zig index 0a2441d1fdf3c7382699d9aa2ccb8535828f4fb6..d0c9588b8dae2b7cf430e1882d8bd44ad84337e8 100644 --- a/std/math/complex/arg.zig +++ b/std/math/complex/arg.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the angular component (in radians) of z. pub fn arg(z: var) @typeOf(z.re) { const T = @typeOf(z.re); return math.atan2(T, z.im, z.re); diff --git a/std/math/complex/asin.zig b/std/math/complex/asin.zig index 6be775d7484c997849a0dead3b2892df9f4bde81..76f94a286c2a1a702aec775857101ae8e965ed2d 100644 --- a/std/math/complex/asin.zig +++ b/std/math/complex/asin.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +// Returns the arc-sine of z. pub fn asin(z: var) Complex(@typeOf(z.re)) { const T = @typeOf(z.re); const x = z.re; diff --git a/std/math/complex/asinh.zig b/std/math/complex/asinh.zig index 8e0903675070c8660e859ce88804f609c612f2c3..da065aad01872012489361aee479e00017d11a7c 100644 --- a/std/math/complex/asinh.zig +++ b/std/math/complex/asinh.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the hyperbolic arc-sine of z. pub fn asinh(z: var) Complex(@typeOf(z.re)) { const T = @typeOf(z.re); const q = Complex(T).new(-z.im, z.re); diff --git a/std/math/complex/atan.zig b/std/math/complex/atan.zig index 6b83adbd9761d392e993aa31cc201468ede8a2b1..89bc8dfaf0956688796dfc8c5783ae95ce42b511 100644 --- a/std/math/complex/atan.zig +++ b/std/math/complex/atan.zig @@ -1,9 +1,16 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/complex/catanf.c +// https://git.musl-libc.org/cgit/musl/tree/src/complex/catan.c + const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the arc-tangent of z. pub fn atan(z: var) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { diff --git a/std/math/complex/atanh.zig b/std/math/complex/atanh.zig index 8edfb6e78e758d86ed49caaaba2fbe02035f5f61..225e7c61deea0ad2c7ef1eb97ddeefaef5f4c5c6 100644 --- a/std/math/complex/atanh.zig +++ b/std/math/complex/atanh.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the hyperbolic arc-tangent of z. pub fn atanh(z: var) Complex(@typeOf(z.re)) { const T = @typeOf(z.re); const q = Complex(T).new(-z.im, z.re); diff --git a/std/math/complex/conj.zig b/std/math/complex/conj.zig index 7a42d365eaf8b8b5b960134bf33c3e1d738be868..bd71ca3c06edf4d7be439e8843714c288bf26a00 100644 --- a/std/math/complex/conj.zig +++ b/std/math/complex/conj.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the complex conjugate of z. pub fn conj(z: var) Complex(@typeOf(z.re)) { const T = @typeOf(z.re); return Complex(T).new(z.re, -z.im); diff --git a/std/math/complex/cos.zig b/std/math/complex/cos.zig index 71f9603c75841ad017d368b531c9f5cd01226860..332009ffe5c7ceb00eec04e5311923c5c44cd677 100644 --- a/std/math/complex/cos.zig +++ b/std/math/complex/cos.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the cosine of z. pub fn cos(z: var) Complex(@typeOf(z.re)) { const T = @typeOf(z.re); const p = Complex(T).new(-z.im, z.re); diff --git a/std/math/complex/cosh.zig b/std/math/complex/cosh.zig index 9806e411709a3130810e94f3aa950918f3114be5..be7bfde963c2a15a869610235d23029d642a1fae 100644 --- a/std/math/complex/cosh.zig +++ b/std/math/complex/cosh.zig @@ -1,3 +1,9 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/complex/ccoshf.c +// https://git.musl-libc.org/cgit/musl/tree/src/complex/ccosh.c + const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; @@ -6,6 +12,7 @@ const Complex = cmath.Complex; const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; +/// Returns the hyperbolic arc-cosine of z. pub fn cosh(z: var) Complex(@typeOf(z.re)) { const T = @typeOf(z.re); return switch (T) { diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig index c74ac2fc08fc4359a23296e6b462a71466c05c2c..9b686bebc3288da2440c90fa8683a3bc406dd6fb 100644 --- a/std/math/complex/exp.zig +++ b/std/math/complex/exp.zig @@ -1,3 +1,9 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/complex/cexpf.c +// https://git.musl-libc.org/cgit/musl/tree/src/complex/cexp.c + const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; @@ -6,6 +12,7 @@ const Complex = cmath.Complex; const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; +/// Returns e raised to the power of z (e^z). pub fn exp(z: var) @typeOf(z) { const T = @typeOf(z.re); diff --git a/std/math/complex/ldexp.zig b/std/math/complex/ldexp.zig index 6b4306bf77277e944c31065ee179ed8abd9f83db..d6f810793f4df1f4704b329b526b655ee5436d1d 100644 --- a/std/math/complex/ldexp.zig +++ b/std/math/complex/ldexp.zig @@ -1,9 +1,16 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/complex/__cexpf.c +// https://git.musl-libc.org/cgit/musl/tree/src/complex/__cexp.c + const std = @import("../../std.zig"); const debug = std.debug; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns exp(z) scaled to avoid overflow. pub fn ldexp_cexp(z: var, expt: i32) @typeOf(z) { const T = @typeOf(z.re); diff --git a/std/math/complex/log.zig b/std/math/complex/log.zig index 2b43a6970f5b09701196ff8fd7b0e86db3519c3b..762b4fde9a0437ade39096c4a6c72e52c3f0598d 100644 --- a/std/math/complex/log.zig +++ b/std/math/complex/log.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the natural logarithm of z. pub fn log(z: var) Complex(@typeOf(z.re)) { const T = @typeOf(z.re); const r = cmath.abs(z); diff --git a/std/math/complex/pow.zig b/std/math/complex/pow.zig index 9174bb36260aca9933b1c2118524bda535c46d11..a2480453fc9b0984390572e8e861bca86b9f52e2 100644 --- a/std/math/complex/pow.zig +++ b/std/math/complex/pow.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns z raised to the complex power of c. pub fn pow(comptime T: type, z: T, c: T) T { const p = cmath.log(z); const q = c.mul(p); diff --git a/std/math/complex/proj.zig b/std/math/complex/proj.zig index aadcff6ff664e4da4636a2ff01934f9ff2b42ea1..c8f2d9fc6de0f6b79618cef2cb9976248bab719c 100644 --- a/std/math/complex/proj.zig +++ b/std/math/complex/proj.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the projection of z onto the riemann sphere. pub fn proj(z: var) Complex(@typeOf(z.re)) { const T = @typeOf(z.re); diff --git a/std/math/complex/sin.zig b/std/math/complex/sin.zig index 049631f31e361557a4e22898316f2fdb1ba140cb..9ddc3a7a8040dd010b60b9dedf5135e069c7a44c 100644 --- a/std/math/complex/sin.zig +++ b/std/math/complex/sin.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the sine of z. pub fn sin(z: var) Complex(@typeOf(z.re)) { const T = @typeOf(z.re); const p = Complex(T).new(-z.im, z.re); diff --git a/std/math/complex/sinh.zig b/std/math/complex/sinh.zig index 0b656e53545dfd12123f2cc22803faea2e62b298..6286d8447ff5f85940642d7f4a23a8228e190e8c 100644 --- a/std/math/complex/sinh.zig +++ b/std/math/complex/sinh.zig @@ -1,3 +1,9 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/complex/csinhf.c +// https://git.musl-libc.org/cgit/musl/tree/src/complex/csinh.c + const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; @@ -6,6 +12,7 @@ const Complex = cmath.Complex; const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; +/// Returns the hyperbolic sine of z. pub fn sinh(z: var) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig index e935d0b238566e9dcfafec8e61780369195c7a77..36f4c28e29c364f9c42cdac0651bed9aa2109d7c 100644 --- a/std/math/complex/sqrt.zig +++ b/std/math/complex/sqrt.zig @@ -1,9 +1,17 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/complex/csqrtf.c +// https://git.musl-libc.org/cgit/musl/tree/src/complex/csqrt.c + const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the square root of z. The real and imaginary parts of the result have the same sign +/// as the imaginary part of z. pub fn sqrt(z: var) @typeOf(z) { const T = @typeOf(z.re); diff --git a/std/math/complex/tan.zig b/std/math/complex/tan.zig index 45e2873eb6ee5460e737888e39086107cb31dd85..398b8295ca1bae426f3e6a95982db22da66664c0 100644 --- a/std/math/complex/tan.zig +++ b/std/math/complex/tan.zig @@ -4,6 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the tanget of z. pub fn tan(z: var) Complex(@typeOf(z.re)) { const T = @typeOf(z.re); const q = Complex(T).new(-z.im, z.re); diff --git a/std/math/complex/tanh.zig b/std/math/complex/tanh.zig index de905ee3f692432c4c8d2aa7a47c5da7ee2a98e9..5c14ec66f2dbf44e2038b4aa110e00a4fb15f4ea 100644 --- a/std/math/complex/tanh.zig +++ b/std/math/complex/tanh.zig @@ -1,9 +1,16 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/complex/ctanhf.c +// https://git.musl-libc.org/cgit/musl/tree/src/complex/ctanh.c + const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; +/// Returns the hyperbolic tangent of z. pub fn tanh(z: var) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { diff --git a/std/math/copysign.zig b/std/math/copysign.zig index ff8140b3ab9efa23fb3b9725b00edf165f1d78da..e4d90c395e67dc7007893c2e529e58b2e69c64db 100644 --- a/std/math/copysign.zig +++ b/std/math/copysign.zig @@ -1,8 +1,15 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/math/copysignf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/copysign.c + const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; +/// Returns a value with the magnitude of x and the sign of y. pub fn copysign(comptime T: type, x: T, y: T) T { return switch (T) { f16 => copysign16(x, y), diff --git a/std/math/cosh.zig b/std/math/cosh.zig index eb3082e5fa179ac605ca911d607c8e84688c76ad..75c5c15ec153b183ea701e2ab76d2482a9507030 100644 --- a/std/math/cosh.zig +++ b/std/math/cosh.zig @@ -1,8 +1,8 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - cosh(+-0) = 1 -// - cosh(+-inf) = +inf -// - cosh(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/coshf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/cosh.c const builtin = @import("builtin"); const std = @import("../std.zig"); @@ -11,6 +11,12 @@ const expo2 = @import("expo2.zig").expo2; const expect = std.testing.expect; const maxInt = std.math.maxInt; +/// Returns the hyperbolic cosine of x. +/// +/// Special Cases: +/// - cosh(+-0) = 1 +/// - cosh(+-inf) = +inf +/// - cosh(nan) = nan pub fn cosh(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/exp.zig b/std/math/exp.zig index aabccffd0b4ed59d0996f290ab9a5c645ea5faa5..ad058646a4a7a1427551b25aa8dee011f7e89378 100644 --- a/std/math/exp.zig +++ b/std/math/exp.zig @@ -1,13 +1,19 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - exp(+inf) = +inf -// - exp(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/expf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/exp.c const std = @import("../std.zig"); const math = std.math; const assert = std.debug.assert; const builtin = @import("builtin"); +/// Returns e raised to the power of x (e^x). +/// +/// Special Cases: +/// - exp(+inf) = +inf +/// - exp(nan) = nan pub fn exp(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/exp2.zig b/std/math/exp2.zig index 392d45bf68baec91cd2b0a35d0f7a7cdf22a677c..07a39576b18bc14eb24429793f75c18c00c6019a 100644 --- a/std/math/exp2.zig +++ b/std/math/exp2.zig @@ -1,12 +1,18 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - exp2(+inf) = +inf -// - exp2(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/exp2f.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/exp2.c const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns 2 raised to the power of x (2^x). +/// +/// Special Cases: +/// - exp2(+inf) = +inf +/// - exp2(nan) = nan pub fn exp2(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/expm1.zig b/std/math/expm1.zig index b83cc3e82e854e4c536dc178f9f37e310586be06..5e347f86f6caf7c204344966e8ae2a16d883d46c 100644 --- a/std/math/expm1.zig +++ b/std/math/expm1.zig @@ -1,14 +1,23 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - expm1(+inf) = +inf -// - expm1(-inf) = -1 -// - expm1(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/expmf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/expm.c + +// TODO: Updated recently. const builtin = @import("builtin"); const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns e raised to the power of x, minus 1 (e^x - 1). This is more accurate than exp(e, x) - 1 +/// when x is near 0. +/// +/// Special Cases: +/// - expm1(+inf) = +inf +/// - expm1(-inf) = -1 +/// - expm1(nan) = nan pub fn expm1(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/expo2.zig b/std/math/expo2.zig index 57e2bf34c940734f559f3b8c7b4466da4611b50f..c00098a5a72ed5b3c13a75082f8c01af1c6d06ca 100644 --- a/std/math/expo2.zig +++ b/std/math/expo2.zig @@ -1,5 +1,12 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/math/__expo2f.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/__expo2.c + const math = @import("../math.zig"); +/// Returns exp(x) / 2 for x >= log(maxFloat(T)). pub fn expo2(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/fabs.zig b/std/math/fabs.zig index e30f788ae736053325fa0f533300a231a38d40bd..6469f388352fdb709164ad1e9ed03adeb944f20c 100644 --- a/std/math/fabs.zig +++ b/std/math/fabs.zig @@ -1,13 +1,19 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - fabs(+-inf) = +inf -// - fabs(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/fabsf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/fabs.c const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; +/// Returns the absolute value of x. +/// +/// Special Cases: +/// - fabs(+-inf) = +inf +/// - fabs(nan) = nan pub fn fabs(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/floor.zig b/std/math/floor.zig index ab45a8fee7462967ae5eed1b61d4b75cfe306a8b..e5ff2b1fc1a74b53d36e474bc243319a5f1dcc75 100644 --- a/std/math/floor.zig +++ b/std/math/floor.zig @@ -1,14 +1,20 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - floor(+-0) = +-0 -// - floor(+-inf) = +-inf -// - floor(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/floorf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/floor.c const builtin = @import("builtin"); const expect = std.testing.expect; const std = @import("../std.zig"); const math = std.math; +/// Returns the greatest integer value less than or equal to x. +/// +/// Special Cases: +/// - floor(+-0) = +-0 +/// - floor(+-inf) = +-inf +/// - floor(nan) = nan pub fn floor(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/frexp.zig b/std/math/frexp.zig index 35eec315d971bd9985e8a1a611aeeffb0e8019ee..2759cd64923d96de294858d56e34eaa6972bfe7f 100644 --- a/std/math/frexp.zig +++ b/std/math/frexp.zig @@ -1,8 +1,8 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - frexp(+-0) = +-0, 0 -// - frexp(+-inf) = +-inf, 0 -// - frexp(nan) = nan, undefined +// https://git.musl-libc.org/cgit/musl/tree/src/math/frexpf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/frexp.c const std = @import("../std.zig"); const math = std.math; @@ -17,6 +17,13 @@ fn frexp_result(comptime T: type) type { pub const frexp32_result = frexp_result(f32); pub const frexp64_result = frexp_result(f64); +/// Breaks x into a normalized fraction and an integral power of two. +/// f == frac * 2^exp, with |frac| in the interval [0.5, 1). +/// +/// Special Cases: +/// - frexp(+-0) = +-0, 0 +/// - frexp(+-inf) = +-inf, 0 +/// - frexp(nan) = nan, undefined pub fn frexp(x: var) frexp_result(@typeOf(x)) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/hypot.zig b/std/math/hypot.zig index fddbe7c0688d22572606c50c7aa7b7619cb0c89b..c15da1495e86201f359d3dba2b8ca734ca948cd2 100644 --- a/std/math/hypot.zig +++ b/std/math/hypot.zig @@ -1,15 +1,21 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - hypot(+-inf, y) = +inf -// - hypot(x, +-inf) = +inf -// - hypot(nan, y) = nan -// - hypot(x, nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/hypotf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/hypot.c const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; +/// Returns sqrt(x * x + y * y), avoiding unncessary overflow and underflow. +/// +/// Special Cases: +/// - hypot(+-inf, y) = +inf +/// - hypot(x, +-inf) = +inf +/// - hypot(nan, y) = nan +/// - hypot(x, nan) = nan pub fn hypot(comptime T: type, x: T, y: T) T { return switch (T) { f32 => hypot32(x, y), diff --git a/std/math/ilogb.zig b/std/math/ilogb.zig index 2dfd42a62c431ac1babc4cf81c41a08e1fe6cbeb..fe4158a6dd13fa311184cf6bd1e8c35fdd74ef7a 100644 --- a/std/math/ilogb.zig +++ b/std/math/ilogb.zig @@ -1,8 +1,8 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - ilogb(+-inf) = maxInt(i32) -// - ilogb(0) = maxInt(i32) -// - ilogb(nan) = maxInt(i32) +// https://git.musl-libc.org/cgit/musl/tree/src/math/ilogbf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/ilogb.c const std = @import("../std.zig"); const math = std.math; @@ -10,6 +10,12 @@ const expect = std.testing.expect; const maxInt = std.math.maxInt; const minInt = std.math.minInt; +/// Returns the binary exponent of x as an integer. +/// +/// Special Cases: +/// - ilogb(+-inf) = maxInt(i32) +/// - ilogb(0) = maxInt(i32) +/// - ilogb(nan) = maxInt(i32) pub fn ilogb(x: var) i32 { const T = @typeOf(x); return switch (T) { diff --git a/std/math/inf.zig b/std/math/inf.zig index e1bfbb197ac9f8461fb233d0fdac761c2fe6602d..86ff2455332ed188f7a9f9d4fbb6e5d598afa810 100644 --- a/std/math/inf.zig +++ b/std/math/inf.zig @@ -1,6 +1,7 @@ const std = @import("../std.zig"); const math = std.math; +/// Returns value inf for the type T. pub fn inf(comptime T: type) T { return switch (T) { f16 => math.inf_f16, diff --git a/std/math/isfinite.zig b/std/math/isfinite.zig index ee8a5ff59072a6eb29cb40101d89545d89cac29f..99eba668f9b4ead5b888defc476b0d797951802c 100644 --- a/std/math/isfinite.zig +++ b/std/math/isfinite.zig @@ -3,6 +3,7 @@ const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; +/// Returns whether x is a finite value. pub fn isFinite(x: var) bool { const T = @typeOf(x); switch (T) { diff --git a/std/math/isinf.zig b/std/math/isinf.zig index 1b1759bd36d2556c3275f049290eed87ceb53a8f..37934f4cf44e1ef9d5845758ca2f89a54805f975 100644 --- a/std/math/isinf.zig +++ b/std/math/isinf.zig @@ -3,6 +3,7 @@ const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; +/// Returns whether x is an infinity, ignoring sign. pub fn isInf(x: var) bool { const T = @typeOf(x); switch (T) { @@ -28,6 +29,7 @@ pub fn isInf(x: var) bool { } } +/// Returns whether x is an infinity with a positive sign. pub fn isPositiveInf(x: var) bool { const T = @typeOf(x); switch (T) { @@ -49,6 +51,7 @@ pub fn isPositiveInf(x: var) bool { } } +/// Returns whether x is an infinity with a negative sign. pub fn isNegativeInf(x: var) bool { const T = @typeOf(x); switch (T) { diff --git a/std/math/isnan.zig b/std/math/isnan.zig index 9e541bf0a256bcec33b739c889478040b55fc0c1..cf8cd2e1c25cffc74e104b895b804cae31dbb9b0 100644 --- a/std/math/isnan.zig +++ b/std/math/isnan.zig @@ -3,13 +3,15 @@ const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; +/// Returns whether x is a nan. pub fn isNan(x: var) bool { return x != x; } -/// Note: A signalling nan is identical to a standard nan right now but may have a different bit -/// representation in the future when required. +/// Returns whether x is a signalling nan. pub fn isSignalNan(x: var) bool { + // Note: A signalling nan is identical to a standard nan right now but may have a different bit + // representation in the future when required. return isNan(x); } diff --git a/std/math/isnormal.zig b/std/math/isnormal.zig index cddcada1d3acfa5e5e661e57e0490f6b081dfa75..f8611ef80528b011e3375c17200dd755b78987c5 100644 --- a/std/math/isnormal.zig +++ b/std/math/isnormal.zig @@ -3,6 +3,7 @@ const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; +// Returns whether x has a normalized representation (i.e. integer part of mantissa is 1). pub fn isNormal(x: var) bool { const T = @typeOf(x); switch (T) { diff --git a/std/math/ln.zig b/std/math/ln.zig index 82b212f00f9c5477e57f62a82f30d51976dcc44a..c5d4c9ff2573218bfc548485dc71d1cfa398e966 100644 --- a/std/math/ln.zig +++ b/std/math/ln.zig @@ -1,9 +1,8 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - ln(+inf) = +inf -// - ln(0) = -inf -// - ln(x) = nan if x < 0 -// - ln(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/lnf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/ln.c const std = @import("../std.zig"); const math = std.math; @@ -11,6 +10,13 @@ const expect = std.testing.expect; const builtin = @import("builtin"); const TypeId = builtin.TypeId; +/// Returns the natural logarithm of x. +/// +/// Special Cases: +/// - ln(+inf) = +inf +/// - ln(0) = -inf +/// - ln(x) = nan if x < 0 +/// - ln(nan) = nan pub fn ln(x: var) @typeOf(x) { const T = @typeOf(x); switch (@typeId(T)) { diff --git a/std/math/log.zig b/std/math/log.zig index 100dc2fb7f9867a002d5bcf0bb8f95e845ab60f9..77f3639fd296403086a46030d6f0f68ca4fcd320 100644 --- a/std/math/log.zig +++ b/std/math/log.zig @@ -1,9 +1,16 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/math/logf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/log.c + const std = @import("../std.zig"); const math = std.math; const builtin = @import("builtin"); const TypeId = builtin.TypeId; const expect = std.testing.expect; +/// Returns the logarithm of x for the provided base. pub fn log(comptime T: type, base: T, x: T) T { if (base == 2) { return math.log2(x); diff --git a/std/math/log10.zig b/std/math/log10.zig index 7311778dddc71ef593547d6a56308340d40082c6..9b0bc3ac52c794c5ee7254cc7162a4761f0cdeb3 100644 --- a/std/math/log10.zig +++ b/std/math/log10.zig @@ -1,9 +1,8 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - log10(+inf) = +inf -// - log10(0) = -inf -// - log10(x) = nan if x < 0 -// - log10(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/log10f.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/log10.c const std = @import("../std.zig"); const math = std.math; @@ -12,6 +11,13 @@ const builtin = @import("builtin"); const TypeId = builtin.TypeId; const maxInt = std.math.maxInt; +/// Returns the base-10 logarithm of x. +/// +/// Special Cases: +/// - log10(+inf) = +inf +/// - log10(0) = -inf +/// - log10(x) = nan if x < 0 +/// - log10(nan) = nan pub fn log10(x: var) @typeOf(x) { const T = @typeOf(x); switch (@typeId(T)) { diff --git a/std/math/log1p.zig b/std/math/log1p.zig index c9be1132be92fddf9afb514c00d83675c0006c68..bae6deb53698a9aa78afa564f05044bfecad1edf 100644 --- a/std/math/log1p.zig +++ b/std/math/log1p.zig @@ -1,16 +1,22 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - log1p(+inf) = +inf -// - log1p(+-0) = +-0 -// - log1p(-1) = -inf -// - log1p(x) = nan if x < -1 -// - log1p(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/log1pf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/log1p.c const builtin = @import("builtin"); const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns the natural logarithm of 1 + x with greater accuracy when x is near zero. +/// +/// Special Cases: +/// - log1p(+inf) = +inf +/// - log1p(+-0) = +-0 +/// - log1p(-1) = -inf +/// - log1p(x) = nan if x < -1 +/// - log1p(nan) = nan pub fn log1p(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/log2.zig b/std/math/log2.zig index e2dbf4105a9b074bd75fc5ddc98629adb7867d67..88450a7ffdd1623abc4dc6d842419017c2d89a4f 100644 --- a/std/math/log2.zig +++ b/std/math/log2.zig @@ -1,9 +1,8 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - log2(+inf) = +inf -// - log2(0) = -inf -// - log2(x) = nan if x < 0 -// - log2(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/log2f.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/log2.c const std = @import("../std.zig"); const math = std.math; @@ -12,6 +11,13 @@ const builtin = @import("builtin"); const TypeId = builtin.TypeId; const maxInt = std.math.maxInt; +/// Returns the base-2 logarithm of x. +/// +/// Special Cases: +/// - log2(+inf) = +inf +/// - log2(0) = -inf +/// - log2(x) = nan if x < 0 +/// - log2(nan) = nan pub fn log2(x: var) @typeOf(x) { const T = @typeOf(x); switch (@typeId(T)) { diff --git a/std/math/modf.zig b/std/math/modf.zig index e5a4964e63901dc3f126d94c4305326482fc95f2..92194d4c7599fd604ab3616f56fdd3531afe302d 100644 --- a/std/math/modf.zig +++ b/std/math/modf.zig @@ -1,7 +1,8 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - modf(+-inf) = +-inf, nan -// - modf(nan) = nan, nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/modff.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/modf.c const std = @import("../std.zig"); const math = std.math; @@ -17,6 +18,12 @@ fn modf_result(comptime T: type) type { pub const modf32_result = modf_result(f32); pub const modf64_result = modf_result(f64); +/// Returns the integer and fractional floating-point numbers that sum to x. The sign of each +/// result is the same as the sign of x. +/// +/// Special Cases: +/// - modf(+-inf) = +-inf, nan +/// - modf(nan) = nan, nan pub fn modf(x: var) modf_result(@typeOf(x)) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/nan.zig b/std/math/nan.zig index 1a11a141d5ae7007c7e00de04c069db34021e36b..5a01a5b3bdac95593a334f3d6cb5029ef68b2565 100644 --- a/std/math/nan.zig +++ b/std/math/nan.zig @@ -1,5 +1,6 @@ const math = @import("../math.zig"); +/// Returns the nan representation for type T. pub fn nan(comptime T: type) T { return switch (T) { f16 => math.nan_f16, @@ -10,9 +11,10 @@ pub fn nan(comptime T: type) T { }; } -// Note: A signalling nan is identical to a standard right now by may have a different bit -// representation in the future when required. +/// Returns the signalling nan representation for type T. pub fn snan(comptime T: type) T { + // Note: A signalling nan is identical to a standard right now by may have a different bit + // representation in the future when required. return switch (T) { f16 => @bitCast(f16, math.nan_u16), f32 => @bitCast(f32, math.nan_u32), diff --git a/std/math/powi.zig b/std/math/powi.zig index b7212efcbf7e98e9e7942defe288dcec61f9052c..d80700e5cdfbb4da4cba0033c56583df45ea9166 100644 --- a/std/math/powi.zig +++ b/std/math/powi.zig @@ -1,12 +1,7 @@ -// Special Cases: +// Based on Rust, which is licensed under the MIT license. +// https://github.com/rust-lang/rust/blob/360432f1e8794de58cd94f34c9c17ad65871e5b5/LICENSE-MIT // -// powi(x, +-0) = 1 for any x -// powi(0, y) = 0 for any y -// powi(1, y) = 1 for any y -// powi(-1, y) = -1 for for y an odd integer -// powi(-1, y) = 1 for for y an even integer -// powi(x, y) = Overflow for for y >= @sizeOf(x) - 1 y > 0 -// powi(x, y) = Underflow for for y > @sizeOf(x) - 1 y < 0 +// https://github.com/rust-lang/rust/blob/360432f1e8794de58cd94f34c9c17ad65871e5b5/src/libcore/num/mod.rs#L3423 const builtin = @import("builtin"); const std = @import("../std.zig"); @@ -14,7 +9,16 @@ const math = std.math; const assert = std.debug.assert; const testing = std.testing; -// This implementation is based on that from the rust stlib +/// Returns the power of x raised by the integer y (x^y). +/// +/// Special Cases: +/// - powi(x, +-0) = 1 for any x +/// - powi(0, y) = 0 for any y +/// - powi(1, y) = 1 for any y +/// - powi(-1, y) = -1 for y an odd integer +/// - powi(-1, y) = 1 for y an even integer +/// - powi(x, y) = Overflow for y >= @sizeOf(x) - 1 or y > 0 +/// - powi(x, y) = Underflow for y > @sizeOf(x) - 1 or y < 0 pub fn powi(comptime T: type, x: T, y: T) (error{ Overflow, Underflow, diff --git a/std/math/round.zig b/std/math/round.zig index 39ff56ca79cee60aa5559e567a601d5605887a24..0b80a46ce5b6abf5be9788cd5250452afca84e7f 100644 --- a/std/math/round.zig +++ b/std/math/round.zig @@ -1,14 +1,20 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - round(+-0) = +-0 -// - round(+-inf) = +-inf -// - round(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/roundf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/round.c const builtin = @import("builtin"); const expect = std.testing.expect; const std = @import("../std.zig"); const math = std.math; +/// Returns x rounded to the nearest integer, rounding half away from zero. +/// +/// Special Cases: +/// - round(+-0) = +-0 +/// - round(+-inf) = +-inf +/// - round(nan) = nan pub fn round(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/scalbn.zig b/std/math/scalbn.zig index d1338f5acb719bec08df3a156957243bfdb72cc7..d5716d621ca33c73a25b3f234e6ad32ca0e3c588 100644 --- a/std/math/scalbn.zig +++ b/std/math/scalbn.zig @@ -1,7 +1,14 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/math/scalbnf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/scalbn.c + const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns x * 2^n. pub fn scalbn(x: var, n: i32) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/signbit.zig b/std/math/signbit.zig index 9727152b07947170033e19c58ac465da94d2dd88..e5c5909292fffa7dbcb355b0d52138b6a5f2f14f 100644 --- a/std/math/signbit.zig +++ b/std/math/signbit.zig @@ -2,6 +2,7 @@ const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; +/// Returns whether x is negative or negative 0. pub fn signbit(x: var) bool { const T = @typeOf(x); return switch (T) { diff --git a/std/math/sinh.zig b/std/math/sinh.zig index cf4363c4a9e3a69a4433b9e056c9e1617a60ddf3..73ee65ea6f0ec5c51581639849f67de4c441ee34 100644 --- a/std/math/sinh.zig +++ b/std/math/sinh.zig @@ -1,8 +1,8 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - sinh(+-0) = +-0 -// - sinh(+-inf) = +-inf -// - sinh(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/sinhf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/sinh.c const builtin = @import("builtin"); const std = @import("../std.zig"); @@ -11,6 +11,12 @@ const expect = std.testing.expect; const expo2 = @import("expo2.zig").expo2; const maxInt = std.math.maxInt; +/// Returns the hyperbolic sine of x. +/// +/// Special Cases: +/// - sinh(+-0) = +-0 +/// - sinh(+-inf) = +-inf +/// - sinh(nan) = nan pub fn sinh(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig index 9062f598a1c7e554b47d2dcb00c99ec59f0a291d..30af5915d40fa98680bf3a09ce4d68b7431beddc 100644 --- a/std/math/sqrt.zig +++ b/std/math/sqrt.zig @@ -1,10 +1,3 @@ -// Special Cases: -// -// - sqrt(+inf) = +inf -// - sqrt(+-0) = +-0 -// - sqrt(x) = nan if x < 0 -// - sqrt(nan) = nan - const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; @@ -12,6 +5,13 @@ const builtin = @import("builtin"); const TypeId = builtin.TypeId; const maxInt = std.math.maxInt; +/// Returns the square root of x. +/// +/// Special Cases: +/// - sqrt(+inf) = +inf +/// - sqrt(+-0) = +-0 +/// - sqrt(x) = nan if x < 0 +/// - sqrt(nan) = nan pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) { const T = @typeOf(x); switch (@typeId(T)) { diff --git a/std/math/tanh.zig b/std/math/tanh.zig index f88ecfdc9c22e7fc696b870f58b6b2f0a10e5cc5..48d26d091e416566eab332bd72ff5ba3808a9151 100644 --- a/std/math/tanh.zig +++ b/std/math/tanh.zig @@ -1,8 +1,8 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - sinh(+-0) = +-0 -// - sinh(+-inf) = +-1 -// - sinh(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/tanhf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/tanh.c const builtin = @import("builtin"); const std = @import("../std.zig"); @@ -11,6 +11,12 @@ const expect = std.testing.expect; const expo2 = @import("expo2.zig").expo2; const maxInt = std.math.maxInt; +/// Returns the hyperbolic tangent of x. +/// +/// Special Cases: +/// - sinh(+-0) = +-0 +/// - sinh(+-inf) = +-1 +/// - sinh(nan) = nan pub fn tanh(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { diff --git a/std/math/trunc.zig b/std/math/trunc.zig index 9449d0e7ebb5737657ebf9e21414fa1aa3d79003..219bcd49146fd7fa0383f1de21aec5dbd5650865 100644 --- a/std/math/trunc.zig +++ b/std/math/trunc.zig @@ -1,14 +1,20 @@ -// Special Cases: +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT // -// - trunc(+-0) = +-0 -// - trunc(+-inf) = +-inf -// - trunc(nan) = nan +// https://git.musl-libc.org/cgit/musl/tree/src/math/truncf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; +/// Returns the integer value of x. +/// +/// Special Cases: +/// - trunc(+-0) = +-0 +/// - trunc(+-inf) = +-inf +/// - trunc(nan) = nan pub fn trunc(x: var) @typeOf(x) { const T = @typeOf(x); return switch (T) { -- 2.54.0 From 3370e60dd96828db6f9d1da36e71064303e97de6 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 2 May 2019 19:03:49 +1200 Subject: [PATCH 3/3] std.math: Correct math.nan usage in cos --- std/math/cos.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/math/cos.zig b/std/math/cos.zig index 6d0cc80c315d8eabbc3ffc282d4b60d98a8e9007..dc4aff59d6ffde27216977f92f88dfdbc7c473d0 100644 --- a/std/math/cos.zig +++ b/std/math/cos.zig @@ -48,7 +48,7 @@ fn cos_(comptime T: type, x_: T) T { var x = x_; if (math.isNan(x) or math.isInf(x)) { - return math.nan(f32); + return math.nan(T); } var sign = false; -- 2.54.0