Merge pull request #2397 from ziglang/std.math

Review std/math and update documentation
master
Marc Tiehuis 2019-05-02 19:05:26 +12:00 committed by GitHub
commit f950ec0c16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 809 additions and 552 deletions

View File

@ -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 std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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) { pub fn acos(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/acoshf.c
// - acosh(nan) = nan // https://git.musl-libc.org/cgit/musl/tree/src/math/acosh.c
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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) { pub fn acosh(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/asinf.c
// - asin(x) = nan if x < -1 or x > 1 // https://git.musl-libc.org/cgit/musl/tree/src/math/asin.c
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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) { pub fn asin(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/asinhf.c
// - asinh(+-inf) = +-inf // https://git.musl-libc.org/cgit/musl/tree/src/math/asinh.c
// - asinh(nan) = nan
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; const expect = std.testing.expect;
const maxInt = std.math.maxInt; 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) { pub fn asinh(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/atanf.c
// - atan(+-inf) = +-pi/2 // https://git.musl-libc.org/cgit/musl/tree/src/math/atan.c
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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) { pub fn atan(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/atan2f.c
// atan2(nan, x) = nan // https://git.musl-libc.org/cgit/musl/tree/src/math/atan2.c
// 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
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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 { pub fn atan2(comptime T: type, y: T, x: T) T {
return switch (T) { return switch (T) {
f32 => atan2_32(y, x), f32 => atan2_32(y, x),

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/atanhf.c
// - atanh(x) = nan if |x| > 1 with signal // https://git.musl-libc.org/cgit/musl/tree/src/math/atanh.c
// - atanh(nan) = nan
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; const expect = std.testing.expect;
const maxInt = std.math.maxInt; 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) { pub fn atanh(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -21,32 +21,49 @@ comptime {
debug.assert(Limb.is_signed == false); 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 { pub const Int = struct {
const sign_bit: usize = 1 << (usize.bit_count - 1); 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, allocator: ?*Allocator,
// - little-endian ordered
// - len >= 1 always /// Raw digits. These are:
// - zero value -> len == 1 with limbs[0] == 0 ///
/// * 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, limbs: []Limb,
// High bit is the sign bit. 1 is negative, 0 positive.
// Remaining bits indicate the number of used limbs. /// 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.
// If Zig gets smarter about packing data, this can be rewritten as a u1 and usize - 1 field.
metadata: usize, 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 { pub fn init(allocator: *Allocator) !Int {
return try Int.initCapacity(allocator, default_capacity); 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 { pub fn initSet(allocator: *Allocator, value: var) !Int {
var s = try Int.init(allocator); var s = try Int.init(allocator);
try s.set(value); try s.set(value);
return s; 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 { pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int {
return Int{ return Int{
.allocator = allocator, .allocator = allocator,
@ -59,14 +76,17 @@ pub const Int = struct {
}; };
} }
/// Returns the number of limbs currently in use.
pub fn len(self: Int) usize { pub fn len(self: Int) usize {
return self.metadata & ~sign_bit; return self.metadata & ~sign_bit;
} }
/// Returns whether an Int is positive.
pub fn isPositive(self: Int) bool { pub fn isPositive(self: Int) bool {
return self.metadata & sign_bit == 0; return self.metadata & sign_bit == 0;
} }
/// Sets the sign of an Int.
pub fn setSign(self: *Int, positive: bool) void { pub fn setSign(self: *Int, positive: bool) void {
if (positive) { if (positive) {
self.metadata &= ~sign_bit; 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 { pub fn setLen(self: *Int, new_len: usize) void {
self.metadata &= sign_bit; self.metadata &= sign_bit;
self.metadata |= new_len; self.metadata |= new_len;
} }
// Initialize an Int directly from a fixed set of limb values. This is considered read-only /// Returns an Int backed by a fixed set of limb values.
// and cannot be used as a receiver argument to any functions. If this tries to allocate /// This is read-only and cannot be used as a result argument. If the Int tries to allocate
// at any point a panic will occur due to the null allocator. /// memory a runtime panic will occur.
pub fn initFixed(limbs: []const Limb) Int { pub fn initFixed(limbs: []const Limb) Int {
var self = Int{ var self = Int{
.allocator = null, .allocator = null,
@ -95,6 +118,9 @@ pub const Int = struct {
return self; 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 { pub fn ensureCapacity(self: *Int, capacity: usize) !void {
self.assertWritable(); self.assertWritable();
if (capacity <= self.limbs.len) { 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 { pub fn deinit(self: *Int) void {
self.assertWritable(); self.assertWritable();
self.allocator.?.free(self.limbs); self.allocator.?.free(self.limbs);
self.* = undefined; 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 { pub fn clone(other: Int) !Int {
other.assertWritable(); other.assertWritable();
return Int{ 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 { pub fn copy(self: *Int, other: Int) !void {
self.assertWritable(); self.assertWritable();
if (self.limbs.ptr == other.limbs.ptr) { if (self.limbs.ptr == other.limbs.ptr) {
@ -140,6 +171,8 @@ pub const Int = struct {
self.metadata = other.metadata; 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 { pub fn swap(self: *Int, other: *Int) void {
self.assertWritable(); self.assertWritable();
mem.swap(Int, self, other); mem.swap(Int, self, other);
@ -152,35 +185,39 @@ pub const Int = struct {
debug.warn("\n"); debug.warn("\n");
} }
/// Negate the sign of an Int.
pub fn negate(self: *Int) void { pub fn negate(self: *Int) void {
self.metadata ^= sign_bit; self.metadata ^= sign_bit;
} }
/// Make an Int positive.
pub fn abs(self: *Int) void { pub fn abs(self: *Int) void {
self.metadata &= ~sign_bit; self.metadata &= ~sign_bit;
} }
/// Returns true if an Int is odd.
pub fn isOdd(self: Int) bool { pub fn isOdd(self: Int) bool {
return self.limbs[0] & 1 != 0; return self.limbs[0] & 1 != 0;
} }
/// Returns true if an Int is even.
pub fn isEven(self: Int) bool { pub fn isEven(self: Int) bool {
return !self.isOdd(); 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 { fn bitCountAbs(self: Int) usize {
return (self.len() - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len() - 1])); 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. /// 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 /// 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 /// 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 /// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
// one greater than the returned value. /// 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. /// 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 { fn bitCountTwosComp(self: Int) usize {
var bits = self.bitCountAbs(); var bits = self.bitCountAbs();
@ -203,7 +240,7 @@ pub const Int = struct {
return bits; 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()) { if (self.eqZero()) {
return true; return true;
} }
@ -215,18 +252,20 @@ pub const Int = struct {
return bit_count >= req_bits; 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 { pub fn fits(self: Int, comptime T: type) bool {
return self.fitsInTwosComp(T.is_signed, T.bit_count); return self.fitsInTwosComp(T.is_signed, T.bit_count);
} }
// Returns the approximate size of the integer in the given base. Negative values accommodate for /// 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 /// 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. /// value. It is inexact and may exceed the given value by ~1-2 bytes.
pub fn sizeInBase(self: Int, base: usize) usize { pub fn sizeInBase(self: Int, base: usize) usize {
const bit_count = usize(@boolToInt(!self.isPositive())) + self.bitCountAbs(); const bit_count = usize(@boolToInt(!self.isPositive())) + self.bitCountAbs();
return (bit_count / math.log2(base)) + 1; 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 { pub fn set(self: *Int, value: var) Allocator.Error!void {
self.assertWritable(); self.assertWritable();
const T = @typeOf(value); const T = @typeOf(value);
@ -290,6 +329,9 @@ pub const Int = struct {
TargetTooSmall, 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 { pub fn to(self: Int, comptime T: type) ConvertError!T {
switch (@typeId(T)) { switch (@typeId(T)) {
TypeId.Int => { 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 { pub fn setString(self: *Int, base: u8, value: []const u8) !void {
self.assertWritable(); self.assertWritable();
if (base < 2 or base > 16) { if (base < 2 or base > 16) {
@ -380,6 +429,8 @@ pub const Int = struct {
self.setSign(positive); 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 /// TODO make this call format instead of the other way around
pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 { pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 {
if (base < 2 or base > 16) { if (base < 2 or base > 16) {
@ -463,7 +514,7 @@ pub const Int = struct {
return s; return s;
} }
/// for the std lib format function /// To allow `std.fmt.printf` to work with Int.
/// TODO make this non-allocating /// TODO make this non-allocating
pub fn format( pub fn format(
self: Int, self: Int,
@ -480,7 +531,7 @@ pub const Int = struct {
return output(context, str); 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 { pub fn cmpAbs(a: Int, b: Int) i8 {
if (a.len() < b.len()) { if (a.len() < b.len()) {
return -1; 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 { pub fn cmp(a: Int, b: Int) i8 {
if (a.isPositive() != b.isPositive()) { if (a.isPositive() != b.isPositive()) {
return if (a.isPositive()) i8(1) else -1; 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 { pub fn eqZero(a: Int) bool {
return a.len() == 1 and a.limbs[0] == 0; 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 { pub fn eqAbs(a: Int, b: Int) bool {
return cmpAbs(a, b) == 0; return cmpAbs(a, b) == 0;
} }
// if a == b /// Returns true if a == b.
pub fn eq(a: Int, b: Int) bool { pub fn eq(a: Int, b: Int) bool {
return cmp(a, b) == 0; 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 { pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void {
r.assertWritable(); r.assertWritable();
if (a.eqZero()) { if (a.eqZero()) {
@ -617,7 +672,11 @@ pub const Int = struct {
r[i] = carry; 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 { pub fn sub(r: *Int, a: Int, b: Int) !void {
r.assertWritable(); r.assertWritable();
if (a.isPositive() != b.isPositive()) { if (a.isPositive() != b.isPositive()) {
@ -684,9 +743,11 @@ pub const Int = struct {
debug.assert(borrow == 0); debug.assert(borrow == 0);
} }
// rma = a * b /// rma = a * b
// ///
// For greatest efficiency, ensure rma does not alias a or 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 { pub fn mul(rma: *Int, a: Int, b: Int) !void {
rma.assertWritable(); 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 { pub fn divFloor(q: *Int, r: *Int, a: Int, b: Int) !void {
try div(q, r, a, b); try div(q, r, a, b);
@ -771,6 +835,9 @@ pub const Int = struct {
r.setSign(b.isPositive()); 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 { pub fn divTrunc(q: *Int, r: *Int, a: Int, b: Int) !void {
try div(q, r, a, b); try div(q, r, a, b);
r.setSign(a.isPositive()); r.setSign(a.isPositive());
@ -969,7 +1036,7 @@ pub const Int = struct {
r.normalize(r.len()); 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 { pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void {
r.assertWritable(); r.assertWritable();
@ -1002,7 +1069,7 @@ pub const Int = struct {
mem.set(Limb, r[0 .. limb_shift - 1], 0); 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 { pub fn shiftRight(r: *Int, a: Int, shift: usize) !void {
r.assertWritable(); 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 { pub fn bitOr(r: *Int, a: Int, b: Int) !void {
r.assertWritable(); 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 { pub fn bitAnd(r: *Int, a: Int, b: Int) !void {
r.assertWritable(); 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 { pub fn bitXor(r: *Int, a: Int, b: Int) !void {
r.assertWritable(); r.assertWritable();
@ -1279,10 +1348,8 @@ test "big.int bitcount/to" {
try a.set(0); try a.set(0);
testing.expect(a.bitCountTwosComp() == 0); testing.expect(a.bitCountTwosComp() == 0);
// TODO: stack smashing testing.expect((try a.to(u0)) == 0);
// testing.expect((try a.to(u0)) == 0); testing.expect((try a.to(i0)) == 0);
// TODO: sigsegv
// testing.expect((try a.to(i0)) == 0);
try a.set(-1); try a.set(-1);
testing.expect(a.bitCountTwosComp() == 1); testing.expect(a.bitCountTwosComp() == 1);

View File

@ -14,11 +14,22 @@ const Limb = bn.Limb;
const DoubleLimb = bn.DoubleLimb; const DoubleLimb = bn.DoubleLimb;
const Int = bn.Int; 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 { 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, p: Int,
/// Denominator. Sign is ignored.
q: Int, 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 { pub fn init(a: *Allocator) !Rational {
return Rational{ return Rational{
.p = try Int.init(a), .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 { pub fn deinit(self: *Rational) void {
self.p.deinit(); self.p.deinit();
self.q.deinit(); self.q.deinit();
} }
/// Set a Rational from a primitive integer type.
pub fn setInt(self: *Rational, a: var) !void { pub fn setInt(self: *Rational, a: var) !void {
try self.p.set(a); try self.p.set(a);
try self.q.set(1); 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 { pub fn setFloatString(self: *Rational, str: []const u8) !void {
// TODO: Accept a/b fractions and exponent form
if (str.len == 0) { if (str.len == 0) {
return error.InvalidFloatString; 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 { 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); debug.assert(@typeId(T) == builtin.TypeId.Float);
const UnsignedIntType = @IntType(false, T.bit_count); const UnsignedIntType = @IntType(false, T.bit_count);
@ -164,8 +180,13 @@ pub const Rational = struct {
try self.reduce(); 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 { 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); debug.assert(@typeId(T) == builtin.TypeId.Float);
const fsize = T.bit_count; const fsize = T.bit_count;
@ -259,6 +280,7 @@ pub const Rational = struct {
return if (self.p.isPositive()) f else -f; 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 { pub fn setRatio(self: *Rational, p: var, q: var) !void {
try self.p.set(p); try self.p.set(p);
try self.q.set(q); 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 { pub fn copyInt(self: *Rational, a: Int) !void {
try self.p.copy(a); try self.p.copy(a);
try self.q.set(1); 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 { pub fn copyRatio(self: *Rational, a: Int, b: Int) !void {
try self.p.copy(a); try self.p.copy(a);
try self.q.copy(b); try self.q.copy(b);
@ -288,23 +312,29 @@ pub const Rational = struct {
try self.reduce(); try self.reduce();
} }
/// Make a Rational positive.
pub fn abs(r: *Rational) void { pub fn abs(r: *Rational) void {
r.p.abs(); r.p.abs();
} }
/// Negate the sign of a Rational.
pub fn negate(r: *Rational) void { pub fn negate(r: *Rational) void {
r.p.negate(); 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 { pub fn swap(r: *Rational, other: *Rational) void {
r.p.swap(&other.p); r.p.swap(&other.p);
r.q.swap(&other.q); 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 { pub fn cmp(a: Rational, b: Rational) !i8 {
return cmpInternal(a, b, true); 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 { pub fn cmpAbs(a: Rational, b: Rational) !i8 {
return cmpInternal(a, b, false); return cmpInternal(a, b, false);
} }
@ -325,9 +355,11 @@ pub const Rational = struct {
return if (is_abs) q.cmpAbs(p) else q.cmp(p); return if (is_abs) q.cmpAbs(p) else q.cmp(p);
} }
// r/q = ap/aq + bp/bq = (ap*bq + bp*aq) / (aq*bq) /// rma = a + b.
// ///
// For best performance, rma should not alias a or 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 { pub fn add(rma: *Rational, a: Rational, b: Rational) !void {
var r = rma; var r = rma;
var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr; 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(); try r.reduce();
} }
// r/q = ap/aq - bp/bq = (ap*bq - bp*aq) / (aq*bq) /// rma = a - b.
// ///
// For best performance, rma should not alias a or 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 { pub fn sub(rma: *Rational, a: Rational, b: Rational) !void {
var r = rma; var r = rma;
var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr; 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(); 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 { pub fn mul(r: *Rational, a: Rational, b: Rational) !void {
try r.p.mul(a.p, b.p); try r.p.mul(a.p, b.p);
try r.q.mul(a.q, b.q); try r.q.mul(a.q, b.q);
try r.reduce(); 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 { pub fn div(r: *Rational, a: Rational, b: Rational) !void {
if (b.p.eqZero()) { if (b.p.eqZero()) {
@panic("division by zero"); @panic("division by zero");
@ -395,7 +437,7 @@ pub const Rational = struct {
try r.reduce(); 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 { pub fn invert(r: *Rational) void {
Int.swap(&r.p, &r.q); Int.swap(&r.p, &r.q);
} }

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/cbrtf.c
// - cbrt(+-inf) = +-inf // https://git.musl-libc.org/cgit/musl/tree/src/math/cbrt.c
// - cbrt(nan) = nan
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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) { pub fn cbrt(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
// - ceil(+-inf) = +-inf // https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c
// - ceil(nan) = nan
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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) { pub fn ceil(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -23,13 +23,18 @@ pub const sqrt = @import("complex/sqrt.zig").sqrt;
pub const tanh = @import("complex/tanh.zig").tanh; pub const tanh = @import("complex/tanh.zig").tanh;
pub const tan = @import("complex/tan.zig").tan; 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 { pub fn Complex(comptime T: type) type {
return struct { return struct {
const Self = @This(); const Self = @This();
/// Real part.
re: T, re: T,
/// Imaginary part.
im: T, im: T,
/// Create a new Complex number from the given real and imaginary parts.
pub fn new(re: T, im: T) Self { pub fn new(re: T, im: T) Self {
return Self{ return Self{
.re = re, .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 { pub fn add(self: Self, other: Self) Self {
return Self{ return Self{
.re = self.re + other.re, .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 { pub fn sub(self: Self, other: Self) Self {
return Self{ return Self{
.re = self.re - other.re, .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 { pub fn mul(self: Self, other: Self) Self {
return Self{ return Self{
.re = self.re * other.re - self.im * other.im, .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 { pub fn div(self: Self, other: Self) Self {
const re_num = self.re * other.re + self.im * other.im; const re_num = self.re * other.re + self.im * other.im;
const im_num = self.im * other.re - self.re * 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 { pub fn conjugate(self: Self) Self {
return Self{ return Self{
.re = self.re, .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 { pub fn reciprocal(self: Self) Self {
const m = self.re * self.re + self.im * self.im; const m = self.re * self.re + self.im * self.im;
return Self{ 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 { pub fn magnitude(self: Self) T {
return math.sqrt(self.re * self.re + self.im * self.im); return math.sqrt(self.re * self.re + self.im * self.im);
} }

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the absolute value (modulus) of z.
pub fn abs(z: var) @typeOf(z.re) { pub fn abs(z: var) @typeOf(z.re) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
return math.hypot(T, z.re, z.im); return math.hypot(T, z.re, z.im);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the arc-cosine of z.
pub fn acos(z: var) Complex(@typeOf(z.re)) { pub fn acos(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
const q = cmath.asin(z); const q = cmath.asin(z);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the hyperbolic arc-cosine of z.
pub fn acosh(z: var) Complex(@typeOf(z.re)) { pub fn acosh(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
const q = cmath.acos(z); const q = cmath.acos(z);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the angular component (in radians) of z.
pub fn arg(z: var) @typeOf(z.re) { pub fn arg(z: var) @typeOf(z.re) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
return math.atan2(T, z.im, z.re); return math.atan2(T, z.im, z.re);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
// Returns the arc-sine of z.
pub fn asin(z: var) Complex(@typeOf(z.re)) { pub fn asin(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
const x = z.re; const x = z.re;

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the hyperbolic arc-sine of z.
pub fn asinh(z: var) Complex(@typeOf(z.re)) { pub fn asinh(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
const q = Complex(T).new(-z.im, z.re); const q = Complex(T).new(-z.im, z.re);

View File

@ -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 std = @import("../../std.zig");
const testing = std.testing; const testing = std.testing;
const math = std.math; const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the arc-tangent of z.
pub fn atan(z: var) @typeOf(z) { pub fn atan(z: var) @typeOf(z) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
return switch (T) { return switch (T) {

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the hyperbolic arc-tangent of z.
pub fn atanh(z: var) Complex(@typeOf(z.re)) { pub fn atanh(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
const q = Complex(T).new(-z.im, z.re); const q = Complex(T).new(-z.im, z.re);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the complex conjugate of z.
pub fn conj(z: var) Complex(@typeOf(z.re)) { pub fn conj(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
return Complex(T).new(z.re, -z.im); return Complex(T).new(z.re, -z.im);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the cosine of z.
pub fn cos(z: var) Complex(@typeOf(z.re)) { pub fn cos(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
const p = Complex(T).new(-z.im, z.re); const p = Complex(T).new(-z.im, z.re);

View File

@ -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 std = @import("../../std.zig");
const testing = std.testing; const testing = std.testing;
const math = std.math; const math = std.math;
@ -6,6 +12,7 @@ const Complex = cmath.Complex;
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
/// Returns the hyperbolic arc-cosine of z.
pub fn cosh(z: var) Complex(@typeOf(z.re)) { pub fn cosh(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
return switch (T) { return switch (T) {

View File

@ -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 std = @import("../../std.zig");
const testing = std.testing; const testing = std.testing;
const math = std.math; const math = std.math;
@ -6,6 +12,7 @@ const Complex = cmath.Complex;
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; 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) { pub fn exp(z: var) @typeOf(z) {
const T = @typeOf(z.re); const T = @typeOf(z.re);

View File

@ -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 std = @import("../../std.zig");
const debug = std.debug; const debug = std.debug;
const math = std.math; const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns exp(z) scaled to avoid overflow.
pub fn ldexp_cexp(z: var, expt: i32) @typeOf(z) { pub fn ldexp_cexp(z: var, expt: i32) @typeOf(z) {
const T = @typeOf(z.re); const T = @typeOf(z.re);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the natural logarithm of z.
pub fn log(z: var) Complex(@typeOf(z.re)) { pub fn log(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
const r = cmath.abs(z); const r = cmath.abs(z);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.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 { pub fn pow(comptime T: type, z: T, c: T) T {
const p = cmath.log(z); const p = cmath.log(z);
const q = c.mul(p); const q = c.mul(p);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the projection of z onto the riemann sphere.
pub fn proj(z: var) Complex(@typeOf(z.re)) { pub fn proj(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re); const T = @typeOf(z.re);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the sine of z.
pub fn sin(z: var) Complex(@typeOf(z.re)) { pub fn sin(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
const p = Complex(T).new(-z.im, z.re); const p = Complex(T).new(-z.im, z.re);

View File

@ -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 std = @import("../../std.zig");
const testing = std.testing; const testing = std.testing;
const math = std.math; const math = std.math;
@ -6,6 +12,7 @@ const Complex = cmath.Complex;
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
/// Returns the hyperbolic sine of z.
pub fn sinh(z: var) @typeOf(z) { pub fn sinh(z: var) @typeOf(z) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
return switch (T) { return switch (T) {

View File

@ -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 std = @import("../../std.zig");
const testing = std.testing; const testing = std.testing;
const math = std.math; const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.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) { pub fn sqrt(z: var) @typeOf(z) {
const T = @typeOf(z.re); const T = @typeOf(z.re);

View File

@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the tanget of z.
pub fn tan(z: var) Complex(@typeOf(z.re)) { pub fn tan(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
const q = Complex(T).new(-z.im, z.re); const q = Complex(T).new(-z.im, z.re);

View File

@ -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 std = @import("../../std.zig");
const testing = std.testing; const testing = std.testing;
const math = std.math; const math = std.math;
const cmath = math.complex; const cmath = math.complex;
const Complex = cmath.Complex; const Complex = cmath.Complex;
/// Returns the hyperbolic tangent of z.
pub fn tanh(z: var) @typeOf(z) { pub fn tanh(z: var) @typeOf(z) {
const T = @typeOf(z.re); const T = @typeOf(z.re);
return switch (T) { return switch (T) {

View File

@ -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 std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; const expect = std.testing.expect;
const maxInt = std.math.maxInt; 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 { pub fn copysign(comptime T: type, x: T, y: T) T {
return switch (T) { return switch (T) {
f16 => copysign16(x, y), f16 => copysign16(x, y),

View File

@ -1,18 +1,23 @@
// Special Cases: // Ported from go, which is licensed under a BSD-3 license.
// https://golang.org/LICENSE
// //
// - cos(+-inf) = nan // https://golang.org/src/math/sin.go
// - cos(nan) = nan
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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) { pub fn cos(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {
f32 => cos32(x), f32 => cos_(f32, x),
f64 => cos64(x), f64 => cos_(f64, x),
else => @compileError("cos not implemented for " ++ @typeName(T)), else => @compileError("cos not implemented for " ++ @typeName(T)),
}; };
} }
@ -33,27 +38,24 @@ const C3 = 2.48015872888517045348E-5;
const C4 = -1.38888888888730564116E-3; const C4 = -1.38888888888730564116E-3;
const C5 = 4.16666666666665929218E-2; const C5 = 4.16666666666665929218E-2;
// NOTE: This is taken from the go stdlib. The musl implementation is much more complex. const pi4a = 7.85398125648498535156e-1;
// const pi4b = 3.77489470793079817668E-8;
// This may have slight differences on some edge cases and may need to replaced if so. const pi4c = 2.69515142907905952645E-15;
fn cos32(x_: f32) f32 { const m4pi = 1.273239544735162542821171882678754627704620361328125;
const pi4a = 7.85398125648498535156e-1;
const pi4b = 3.77489470793079817668E-8; fn cos_(comptime T: type, x_: T) T {
const pi4c = 2.69515142907905952645E-15; const I = @IntType(true, T.bit_count);
const m4pi = 1.273239544735162542821171882678754627704620361328125;
var x = x_; var x = x_;
if (math.isNan(x) or math.isInf(x)) { if (math.isNan(x) or math.isInf(x)) {
return math.nan(f32); return math.nan(T);
} }
var sign = false; var sign = false;
if (x < 0) { x = math.fabs(x);
x = -x;
}
var y = math.floor(x * m4pi); var y = math.floor(x * m4pi);
var j = @floatToInt(i64, y); var j = @floatToInt(I, y);
if (j & 1 == 1) { if (j & 1 == 1) {
j += 1; j += 1;
@ -72,107 +74,51 @@ fn cos32(x_: f32) f32 {
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
const w = z * z; const w = z * z;
const r = r: { const r = if (j == 1 or j == 2)
if (j == 1 or j == 2) { z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))))
break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))); else
} else { 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
}
};
if (sign) { return if (sign) -r else r;
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;
}
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;
}
} }
test "math.cos" { test "math.cos" {
expect(cos(f32(0.0)) == cos32(0.0)); expect(cos(f32(0.0)) == cos_(f32, 0.0));
expect(cos(f64(0.0)) == cos64(0.0)); expect(cos(f64(0.0)) == cos_(f64, 0.0));
} }
test "math.cos32" { test "math.cos32" {
const epsilon = 0.000001; const epsilon = 0.000001;
expect(math.approxEq(f32, cos32(0.0), 1.0, epsilon)); expect(math.approxEq(f32, cos_(f32, 0.0), 1.0, epsilon));
expect(math.approxEq(f32, cos32(0.2), 0.980067, epsilon)); expect(math.approxEq(f32, cos_(f32, 0.2), 0.980067, epsilon));
expect(math.approxEq(f32, cos32(0.8923), 0.627623, epsilon)); expect(math.approxEq(f32, cos_(f32, 0.8923), 0.627623, epsilon));
expect(math.approxEq(f32, cos32(1.5), 0.070737, epsilon)); expect(math.approxEq(f32, cos_(f32, 1.5), 0.070737, epsilon));
expect(math.approxEq(f32, cos32(37.45), 0.969132, epsilon)); expect(math.approxEq(f32, cos_(f32, -1.5), 0.070737, epsilon));
expect(math.approxEq(f32, cos32(89.123), 0.400798, 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" { test "math.cos64" {
const epsilon = 0.000001; const epsilon = 0.000001;
expect(math.approxEq(f64, cos64(0.0), 1.0, epsilon)); expect(math.approxEq(f64, cos_(f64, 0.0), 1.0, epsilon));
expect(math.approxEq(f64, cos64(0.2), 0.980067, epsilon)); expect(math.approxEq(f64, cos_(f64, 0.2), 0.980067, epsilon));
expect(math.approxEq(f64, cos64(0.8923), 0.627623, epsilon)); expect(math.approxEq(f64, cos_(f64, 0.8923), 0.627623, epsilon));
expect(math.approxEq(f64, cos64(1.5), 0.070737, epsilon)); expect(math.approxEq(f64, cos_(f64, 1.5), 0.070737, epsilon));
expect(math.approxEq(f64, cos64(37.45), 0.969132, epsilon)); expect(math.approxEq(f64, cos_(f64, -1.5), 0.070737, epsilon));
expect(math.approxEq(f64, cos64(89.123), 0.40080, 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" { test "math.cos32.special" {
expect(math.isNan(cos32(math.inf(f32)))); expect(math.isNan(cos_(f32, math.inf(f32))));
expect(math.isNan(cos32(-math.inf(f32)))); expect(math.isNan(cos_(f32, -math.inf(f32))));
expect(math.isNan(cos32(math.nan(f32)))); expect(math.isNan(cos_(f32, math.nan(f32))));
} }
test "math.cos64.special" { test "math.cos64.special" {
expect(math.isNan(cos64(math.inf(f64)))); expect(math.isNan(cos_(f64, math.inf(f64))));
expect(math.isNan(cos64(-math.inf(f64)))); expect(math.isNan(cos_(f64, -math.inf(f64))));
expect(math.isNan(cos64(math.nan(f64)))); expect(math.isNan(cos_(f64, math.nan(f64))));
} }

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/coshf.c
// - cosh(+-inf) = +inf // https://git.musl-libc.org/cgit/musl/tree/src/math/cosh.c
// - cosh(nan) = nan
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("../std.zig"); const std = @import("../std.zig");
@ -11,6 +11,12 @@ const expo2 = @import("expo2.zig").expo2;
const expect = std.testing.expect; const expect = std.testing.expect;
const maxInt = std.math.maxInt; 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) { pub fn cosh(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/expf.c
// - exp(nan) = nan // https://git.musl-libc.org/cgit/musl/tree/src/math/exp.c
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const assert = std.debug.assert; const assert = std.debug.assert;
const builtin = @import("builtin"); 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) { pub fn exp(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/exp2f.c
// - exp2(nan) = nan // https://git.musl-libc.org/cgit/musl/tree/src/math/exp2.c
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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) { pub fn exp2(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/expmf.c
// - expm1(-inf) = -1 // https://git.musl-libc.org/cgit/musl/tree/src/math/expm.c
// - expm1(nan) = nan
// TODO: Updated recently.
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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) { pub fn expm1(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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"); const math = @import("../math.zig");
/// Returns exp(x) / 2 for x >= log(maxFloat(T)).
pub fn expo2(x: var) @typeOf(x) { pub fn expo2(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/fabsf.c
// - fabs(nan) = nan // https://git.musl-libc.org/cgit/musl/tree/src/math/fabs.c
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; const expect = std.testing.expect;
const maxInt = std.math.maxInt; 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) { pub fn fabs(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/floorf.c
// - floor(+-inf) = +-inf // https://git.musl-libc.org/cgit/musl/tree/src/math/floor.c
// - floor(nan) = nan
const builtin = @import("builtin"); const builtin = @import("builtin");
const expect = std.testing.expect; const expect = std.testing.expect;
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; 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) { pub fn floor(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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 { pub fn fma(comptime T: type, x: T, y: T, z: T) T {
return switch (T) { return switch (T) {
f32 => fma32(x, y, z), 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 u = @bitCast(u64, xy_z);
const e = (u >> 52) & 0x7FF; 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); return @floatCast(f32, xy_z);
} else { } else {
// TODO: Handle inexact case with double-rounding // 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 { fn fma64(x: f64, y: f64, z: f64) f64 {
if (!math.isFinite(x) or !math.isFinite(y)) { if (!math.isFinite(x) or !math.isFinite(y)) {
return x * y + z; return x * y + z;

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/frexpf.c
// - frexp(+-inf) = +-inf, 0 // https://git.musl-libc.org/cgit/musl/tree/src/math/frexp.c
// - frexp(nan) = nan, undefined
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
@ -17,6 +17,13 @@ fn frexp_result(comptime T: type) type {
pub const frexp32_result = frexp_result(f32); pub const frexp32_result = frexp_result(f32);
pub const frexp64_result = frexp_result(f64); 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)) { pub fn frexp(x: var) frexp_result(@typeOf(x)) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/hypotf.c
// - hypot(x, +-inf) = +inf // https://git.musl-libc.org/cgit/musl/tree/src/math/hypot.c
// - hypot(nan, y) = nan
// - hypot(x, nan) = nan
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; const expect = std.testing.expect;
const maxInt = std.math.maxInt; 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 { pub fn hypot(comptime T: type, x: T, y: T) T {
return switch (T) { return switch (T) {
f32 => hypot32(x, y), f32 => hypot32(x, y),

View File

@ -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) // https://git.musl-libc.org/cgit/musl/tree/src/math/ilogbf.c
// - ilogb(0) = maxInt(i32) // https://git.musl-libc.org/cgit/musl/tree/src/math/ilogb.c
// - ilogb(nan) = maxInt(i32)
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
@ -10,6 +10,12 @@ const expect = std.testing.expect;
const maxInt = std.math.maxInt; const maxInt = std.math.maxInt;
const minInt = std.math.minInt; 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 { pub fn ilogb(x: var) i32 {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -1,6 +1,7 @@
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
/// Returns value inf for the type T.
pub fn inf(comptime T: type) T { pub fn inf(comptime T: type) T {
return switch (T) { return switch (T) {
f16 => math.inf_f16, f16 => math.inf_f16,

View File

@ -3,6 +3,7 @@ const math = std.math;
const expect = std.testing.expect; const expect = std.testing.expect;
const maxInt = std.math.maxInt; const maxInt = std.math.maxInt;
/// Returns whether x is a finite value.
pub fn isFinite(x: var) bool { pub fn isFinite(x: var) bool {
const T = @typeOf(x); const T = @typeOf(x);
switch (T) { switch (T) {

View File

@ -3,6 +3,7 @@ const math = std.math;
const expect = std.testing.expect; const expect = std.testing.expect;
const maxInt = std.math.maxInt; const maxInt = std.math.maxInt;
/// Returns whether x is an infinity, ignoring sign.
pub fn isInf(x: var) bool { pub fn isInf(x: var) bool {
const T = @typeOf(x); const T = @typeOf(x);
switch (T) { 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 { pub fn isPositiveInf(x: var) bool {
const T = @typeOf(x); const T = @typeOf(x);
switch (T) { 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 { pub fn isNegativeInf(x: var) bool {
const T = @typeOf(x); const T = @typeOf(x);
switch (T) { switch (T) {

View File

@ -3,13 +3,15 @@ const math = std.math;
const expect = std.testing.expect; const expect = std.testing.expect;
const maxInt = std.math.maxInt; const maxInt = std.math.maxInt;
/// Returns whether x is a nan.
pub fn isNan(x: var) bool { pub fn isNan(x: var) bool {
return x != x; return x != x;
} }
/// Note: A signalling nan is identical to a standard nan right now but may have a different bit /// Returns whether x is a signalling nan.
/// representation in the future when required.
pub fn isSignalNan(x: var) bool { 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); return isNan(x);
} }

View File

@ -3,6 +3,7 @@ const math = std.math;
const expect = std.testing.expect; const expect = std.testing.expect;
const maxInt = std.math.maxInt; 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 { pub fn isNormal(x: var) bool {
const T = @typeOf(x); const T = @typeOf(x);
switch (T) { switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/lnf.c
// - ln(0) = -inf // https://git.musl-libc.org/cgit/musl/tree/src/math/ln.c
// - ln(x) = nan if x < 0
// - ln(nan) = nan
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
@ -11,6 +10,13 @@ const expect = std.testing.expect;
const builtin = @import("builtin"); const builtin = @import("builtin");
const TypeId = builtin.TypeId; 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) { pub fn ln(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
switch (@typeId(T)) { switch (@typeId(T)) {

View File

@ -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 std = @import("../std.zig");
const math = std.math; const math = std.math;
const builtin = @import("builtin"); const builtin = @import("builtin");
const TypeId = builtin.TypeId; const TypeId = builtin.TypeId;
const expect = std.testing.expect; 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 { pub fn log(comptime T: type, base: T, x: T) T {
if (base == 2) { if (base == 2) {
return math.log2(x); return math.log2(x);

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/log10f.c
// - log10(0) = -inf // https://git.musl-libc.org/cgit/musl/tree/src/math/log10.c
// - log10(x) = nan if x < 0
// - log10(nan) = nan
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
@ -12,6 +11,13 @@ const builtin = @import("builtin");
const TypeId = builtin.TypeId; const TypeId = builtin.TypeId;
const maxInt = std.math.maxInt; 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) { pub fn log10(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
switch (@typeId(T)) { switch (@typeId(T)) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/log1pf.c
// - log1p(+-0) = +-0 // https://git.musl-libc.org/cgit/musl/tree/src/math/log1p.c
// - log1p(-1) = -inf
// - log1p(x) = nan if x < -1
// - log1p(nan) = nan
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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) { pub fn log1p(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/log2f.c
// - log2(0) = -inf // https://git.musl-libc.org/cgit/musl/tree/src/math/log2.c
// - log2(x) = nan if x < 0
// - log2(nan) = nan
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
@ -12,6 +11,13 @@ const builtin = @import("builtin");
const TypeId = builtin.TypeId; const TypeId = builtin.TypeId;
const maxInt = std.math.maxInt; 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) { pub fn log2(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
switch (@typeId(T)) { switch (@typeId(T)) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/modff.c
// - modf(nan) = nan, nan // https://git.musl-libc.org/cgit/musl/tree/src/math/modf.c
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
@ -17,6 +18,12 @@ fn modf_result(comptime T: type) type {
pub const modf32_result = modf_result(f32); pub const modf32_result = modf_result(f32);
pub const modf64_result = modf_result(f64); 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)) { pub fn modf(x: var) modf_result(@typeOf(x)) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -1,5 +1,6 @@
const math = @import("../math.zig"); const math = @import("../math.zig");
/// Returns the nan representation for type T.
pub fn nan(comptime T: type) T { pub fn nan(comptime T: type) T {
return switch (T) { return switch (T) {
f16 => math.nan_f16, 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 /// Returns the signalling nan representation for type T.
// representation in the future when required.
pub fn snan(comptime T: 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) { return switch (T) {
f16 => @bitCast(f16, math.nan_u16), f16 => @bitCast(f16, math.nan_u16),
f32 => @bitCast(f32, math.nan_u32), f32 => @bitCast(f32, math.nan_u32),

View File

@ -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 // https://golang.org/src/math/pow.go
// 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
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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 { pub fn pow(comptime T: type, x: T, y: T) T {
if (@typeInfo(T) == builtin.TypeId.Int) { if (@typeInfo(T) == builtin.TypeId.Int) {
return math.powi(T, x, y) catch unreachable; 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; 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 (x == 0) {
if (y < 0) { if (y < 0) {
// pow(+-0, y) = +- 0 for y an odd integer // 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; // special case sqrt
var flip = false; if (y == 0.5) {
if (ay < 0) { return math.sqrt(x);
ay = -ay;
flip = true;
} }
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 yi = r1.ipart;
var yf = r1.fpart; var yf = r1.fpart;
@ -148,8 +145,18 @@ pub fn pow(comptime T: type, x: T, y: T) T {
var xe = r2.exponent; var xe = r2.exponent;
var x1 = r2.significand; var x1 = r2.significand;
var i = @floatToInt(i32, yi); var i = @floatToInt(@IntType(true, T.bit_count), yi);
while (i != 0) : (i >>= 1) { 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) { if (i & 1 == 1) {
a1 *= x1; a1 *= x1;
ae += xe; ae += xe;
@ -163,7 +170,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
} }
// a *= a1 * 2^ae // a *= a1 * 2^ae
if (flip) { if (y < 0) {
a1 = 1 / a1; a1 = 1 / a1;
ae = -ae; 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(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.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.isNan(pow(f32, 5.0, math.nan(f32))));
expect(math.isPositiveInf(pow(f32, 0.0, -1.0))); expect(math.isPositiveInf(pow(f32, 0.0, -1.0)));
//expect(math.isNegativeInf(pow(f32, -0.0, -3.0))); TODO is this required? //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, -1.0, 1.2)));
expect(math.isNan(pow(f32, -12.4, 78.5))); 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))));
}

View File

@ -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 // https://github.com/rust-lang/rust/blob/360432f1e8794de58cd94f34c9c17ad65871e5b5/src/libcore/num/mod.rs#L3423
// 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
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("../std.zig"); const std = @import("../std.zig");
@ -14,7 +9,16 @@ const math = std.math;
const assert = std.debug.assert; const assert = std.debug.assert;
const testing = std.testing; 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{ pub fn powi(comptime T: type, x: T, y: T) (error{
Overflow, Overflow,
Underflow, Underflow,

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/roundf.c
// - round(+-inf) = +-inf // https://git.musl-libc.org/cgit/musl/tree/src/math/round.c
// - round(nan) = nan
const builtin = @import("builtin"); const builtin = @import("builtin");
const expect = std.testing.expect; const expect = std.testing.expect;
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; 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) { pub fn round(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; const expect = std.testing.expect;
/// Returns x * 2^n.
pub fn scalbn(x: var, n: i32) @typeOf(x) { pub fn scalbn(x: var, n: i32) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -2,6 +2,7 @@ const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; const expect = std.testing.expect;
/// Returns whether x is negative or negative 0.
pub fn signbit(x: var) bool { pub fn signbit(x: var) bool {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -1,19 +1,24 @@
// Special Cases: // Ported from go, which is licensed under a BSD-3 license.
// https://golang.org/LICENSE
// //
// - sin(+-0) = +-0 // https://golang.org/src/math/sin.go
// - sin(+-inf) = nan
// - sin(nan) = nan
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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) { pub fn sin(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {
f32 => sin32(x), f32 => sin_(T, x),
f64 => sin64(x), f64 => sin_(T, x),
else => @compileError("sin not implemented for " ++ @typeName(T)), else => @compileError("sin not implemented for " ++ @typeName(T)),
}; };
} }
@ -34,31 +39,27 @@ const C3 = 2.48015872888517045348E-5;
const C4 = -1.38888888888730564116E-3; const C4 = -1.38888888888730564116E-3;
const C5 = 4.16666666666665929218E-2; const C5 = 4.16666666666665929218E-2;
// NOTE: This is taken from the go stdlib. The musl implementation is much more complex. const pi4a = 7.85398125648498535156e-1;
// const pi4b = 3.77489470793079817668E-8;
// This may have slight differences on some edge cases and may need to replaced if so. const pi4c = 2.69515142907905952645E-15;
fn sin32(x_: f32) f32 { const m4pi = 1.273239544735162542821171882678754627704620361328125;
const pi4a = 7.85398125648498535156e-1;
const pi4b = 3.77489470793079817668E-8; fn sin_(comptime T: type, x_: T) T {
const pi4c = 2.69515142907905952645E-15; const I = @IntType(true, T.bit_count);
const m4pi = 1.273239544735162542821171882678754627704620361328125;
var x = x_; var x = x_;
if (x == 0 or math.isNan(x)) { if (x == 0 or math.isNan(x)) {
return x; return x;
} }
if (math.isInf(x)) { if (math.isInf(x)) {
return math.nan(f32); return math.nan(T);
} }
var sign = false; var sign = x < 0;
if (x < 0) { x = math.fabs(x);
x = -x;
sign = true;
}
var y = math.floor(x * m4pi); var y = math.floor(x * m4pi);
var j = @floatToInt(i64, y); var j = @floatToInt(I, y);
if (j & 1 == 1) { if (j & 1 == 1) {
j += 1; j += 1;
@ -74,113 +75,56 @@ fn sin32(x_: f32) f32 {
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
const w = z * z; const w = z * z;
const r = r: { const r = if (j == 1 or j == 2)
if (j == 1 or j == 2) { 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))))
break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))); else
} else { z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
}
};
if (sign) { return if (sign) -r else r;
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;
var x = x_;
if (x == 0 or math.isNan(x)) {
return x;
}
if (math.isInf(x)) {
return math.nan(f64);
}
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;
}
} }
test "math.sin" { test "math.sin" {
expect(sin(f32(0.0)) == sin32(0.0)); expect(sin(f32(0.0)) == sin_(f32, 0.0));
expect(sin(f64(0.0)) == sin64(0.0)); expect(sin(f64(0.0)) == sin_(f64, 0.0));
expect(comptime (math.sin(f64(2))) == math.sin(f64(2))); expect(comptime (math.sin(f64(2))) == math.sin(f64(2)));
} }
test "math.sin32" { test "math.sin32" {
const epsilon = 0.000001; const epsilon = 0.000001;
expect(math.approxEq(f32, sin32(0.0), 0.0, epsilon)); expect(math.approxEq(f32, sin_(f32, 0.0), 0.0, epsilon));
expect(math.approxEq(f32, sin32(0.2), 0.198669, epsilon)); expect(math.approxEq(f32, sin_(f32, 0.2), 0.198669, epsilon));
expect(math.approxEq(f32, sin32(0.8923), 0.778517, epsilon)); expect(math.approxEq(f32, sin_(f32, 0.8923), 0.778517, epsilon));
expect(math.approxEq(f32, sin32(1.5), 0.997495, epsilon)); expect(math.approxEq(f32, sin_(f32, 1.5), 0.997495, epsilon));
expect(math.approxEq(f32, sin32(37.45), -0.246544, epsilon)); expect(math.approxEq(f32, sin_(f32, -1.5), -0.997495, epsilon));
expect(math.approxEq(f32, sin32(89.123), 0.916166, 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" { test "math.sin64" {
const epsilon = 0.000001; const epsilon = 0.000001;
expect(math.approxEq(f64, sin64(0.0), 0.0, epsilon)); expect(math.approxEq(f64, sin_(f64, 0.0), 0.0, epsilon));
expect(math.approxEq(f64, sin64(0.2), 0.198669, epsilon)); expect(math.approxEq(f64, sin_(f64, 0.2), 0.198669, epsilon));
expect(math.approxEq(f64, sin64(0.8923), 0.778517, epsilon)); expect(math.approxEq(f64, sin_(f64, 0.8923), 0.778517, epsilon));
expect(math.approxEq(f64, sin64(1.5), 0.997495, epsilon)); expect(math.approxEq(f64, sin_(f64, 1.5), 0.997495, epsilon));
expect(math.approxEq(f64, sin64(37.45), -0.246543, epsilon)); expect(math.approxEq(f64, sin_(f64, -1.5), -0.997495, epsilon));
expect(math.approxEq(f64, sin64(89.123), 0.916166, 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" { test "math.sin32.special" {
expect(sin32(0.0) == 0.0); expect(sin_(f32, 0.0) == 0.0);
expect(sin32(-0.0) == -0.0); expect(sin_(f32, -0.0) == -0.0);
expect(math.isNan(sin32(math.inf(f32)))); expect(math.isNan(sin_(f32, math.inf(f32))));
expect(math.isNan(sin32(-math.inf(f32)))); expect(math.isNan(sin_(f32, -math.inf(f32))));
expect(math.isNan(sin32(math.nan(f32)))); expect(math.isNan(sin_(f32, math.nan(f32))));
} }
test "math.sin64.special" { test "math.sin64.special" {
expect(sin64(0.0) == 0.0); expect(sin_(f64, 0.0) == 0.0);
expect(sin64(-0.0) == -0.0); expect(sin_(f64, -0.0) == -0.0);
expect(math.isNan(sin64(math.inf(f64)))); expect(math.isNan(sin_(f64, math.inf(f64))));
expect(math.isNan(sin64(-math.inf(f64)))); expect(math.isNan(sin_(f64, -math.inf(f64))));
expect(math.isNan(sin64(math.nan(f64)))); expect(math.isNan(sin_(f64, math.nan(f64))));
} }

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/sinhf.c
// - sinh(+-inf) = +-inf // https://git.musl-libc.org/cgit/musl/tree/src/math/sinh.c
// - sinh(nan) = nan
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("../std.zig"); const std = @import("../std.zig");
@ -11,6 +11,12 @@ const expect = std.testing.expect;
const expo2 = @import("expo2.zig").expo2; const expo2 = @import("expo2.zig").expo2;
const maxInt = std.math.maxInt; 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) { pub fn sinh(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; const expect = std.testing.expect;
@ -12,6 +5,13 @@ const builtin = @import("builtin");
const TypeId = builtin.TypeId; const TypeId = builtin.TypeId;
const maxInt = std.math.maxInt; 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)) { 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); const T = @typeOf(x);
switch (@typeId(T)) { switch (@typeId(T)) {

View File

@ -1,19 +1,24 @@
// Special Cases: // Ported from go, which is licensed under a BSD-3 license.
// https://golang.org/LICENSE
// //
// - tan(+-0) = +-0 // https://golang.org/src/math/tan.go
// - tan(+-inf) = nan
// - tan(nan) = nan
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; 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) { pub fn tan(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {
f32 => tan32(x), f32 => tan_(f32, x),
f64 => tan64(x), f64 => tan_(f64, x),
else => @compileError("tan not implemented for " ++ @typeName(T)), else => @compileError("tan not implemented for " ++ @typeName(T)),
}; };
} }
@ -27,31 +32,27 @@ const Tq2 = -1.32089234440210967447E6;
const Tq3 = 2.50083801823357915839E7; const Tq3 = 2.50083801823357915839E7;
const Tq4 = -5.38695755929454629881E7; const Tq4 = -5.38695755929454629881E7;
// NOTE: This is taken from the go stdlib. The musl implementation is much more complex. const pi4a = 7.85398125648498535156e-1;
// const pi4b = 3.77489470793079817668E-8;
// This may have slight differences on some edge cases and may need to replaced if so. const pi4c = 2.69515142907905952645E-15;
fn tan32(x_: f32) f32 { const m4pi = 1.273239544735162542821171882678754627704620361328125;
const pi4a = 7.85398125648498535156e-1;
const pi4b = 3.77489470793079817668E-8; fn tan_(comptime T: type, x_: T) T {
const pi4c = 2.69515142907905952645E-15; const I = @IntType(true, T.bit_count);
const m4pi = 1.273239544735162542821171882678754627704620361328125;
var x = x_; var x = x_;
if (x == 0 or math.isNan(x)) { if (x == 0 or math.isNan(x)) {
return x; return x;
} }
if (math.isInf(x)) { if (math.isInf(x)) {
return math.nan(f32); return math.nan(T);
} }
var sign = false; var sign = x < 0;
if (x < 0) { x = math.fabs(x);
x = -x;
sign = true;
}
var y = math.floor(x * m4pi); var y = math.floor(x * m4pi);
var j = @floatToInt(i64, y); var j = @floatToInt(I, y);
if (j & 1 == 1) { if (j & 1 == 1) {
j += 1; j += 1;
@ -61,112 +62,57 @@ fn tan32(x_: f32) f32 {
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
const w = z * z; const w = z * z;
var r = r: { var r = if (w > 1e-14)
if (w > 1e-14) { z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4))
break :r z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4)); else
} else { z;
break :r z;
}
};
if (j & 2 == 2) { if (j & 2 == 2) {
r = -1 / r; r = -1 / r;
} }
if (sign) {
r = -r;
}
return r; return if (sign) -r else 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;
var x = x_;
if (x == 0 or math.isNan(x)) {
return x;
}
if (math.isInf(x)) {
return math.nan(f64);
}
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;
} }
test "math.tan" { test "math.tan" {
expect(tan(f32(0.0)) == tan32(0.0)); expect(tan(f32(0.0)) == tan_(f32, 0.0));
expect(tan(f64(0.0)) == tan64(0.0)); expect(tan(f64(0.0)) == tan_(f64, 0.0));
} }
test "math.tan32" { test "math.tan32" {
const epsilon = 0.000001; const epsilon = 0.000001;
expect(math.approxEq(f32, tan32(0.0), 0.0, epsilon)); expect(math.approxEq(f32, tan_(f32, 0.0), 0.0, epsilon));
expect(math.approxEq(f32, tan32(0.2), 0.202710, epsilon)); expect(math.approxEq(f32, tan_(f32, 0.2), 0.202710, epsilon));
expect(math.approxEq(f32, tan32(0.8923), 1.240422, epsilon)); expect(math.approxEq(f32, tan_(f32, 0.8923), 1.240422, epsilon));
expect(math.approxEq(f32, tan32(1.5), 14.101420, epsilon)); expect(math.approxEq(f32, tan_(f32, 1.5), 14.101420, epsilon));
expect(math.approxEq(f32, tan32(37.45), -0.254397, epsilon)); expect(math.approxEq(f32, tan_(f32, 37.45), -0.254397, epsilon));
expect(math.approxEq(f32, tan32(89.123), 2.285852, epsilon)); expect(math.approxEq(f32, tan_(f32, 89.123), 2.285852, epsilon));
} }
test "math.tan64" { test "math.tan64" {
const epsilon = 0.000001; const epsilon = 0.000001;
expect(math.approxEq(f64, tan64(0.0), 0.0, epsilon)); expect(math.approxEq(f64, tan_(f64, 0.0), 0.0, epsilon));
expect(math.approxEq(f64, tan64(0.2), 0.202710, epsilon)); expect(math.approxEq(f64, tan_(f64, 0.2), 0.202710, epsilon));
expect(math.approxEq(f64, tan64(0.8923), 1.240422, epsilon)); expect(math.approxEq(f64, tan_(f64, 0.8923), 1.240422, epsilon));
expect(math.approxEq(f64, tan64(1.5), 14.101420, epsilon)); expect(math.approxEq(f64, tan_(f64, 1.5), 14.101420, epsilon));
expect(math.approxEq(f64, tan64(37.45), -0.254397, epsilon)); expect(math.approxEq(f64, tan_(f64, 37.45), -0.254397, epsilon));
expect(math.approxEq(f64, tan64(89.123), 2.2858376, epsilon)); expect(math.approxEq(f64, tan_(f64, 89.123), 2.2858376, epsilon));
} }
test "math.tan32.special" { test "math.tan32.special" {
expect(tan32(0.0) == 0.0); expect(tan_(f32, 0.0) == 0.0);
expect(tan32(-0.0) == -0.0); expect(tan_(f32, -0.0) == -0.0);
expect(math.isNan(tan32(math.inf(f32)))); expect(math.isNan(tan_(f32, math.inf(f32))));
expect(math.isNan(tan32(-math.inf(f32)))); expect(math.isNan(tan_(f32, -math.inf(f32))));
expect(math.isNan(tan32(math.nan(f32)))); expect(math.isNan(tan_(f32, math.nan(f32))));
} }
test "math.tan64.special" { test "math.tan64.special" {
expect(tan64(0.0) == 0.0); expect(tan_(f64, 0.0) == 0.0);
expect(tan64(-0.0) == -0.0); expect(tan_(f64, -0.0) == -0.0);
expect(math.isNan(tan64(math.inf(f64)))); expect(math.isNan(tan_(f64, math.inf(f64))));
expect(math.isNan(tan64(-math.inf(f64)))); expect(math.isNan(tan_(f64, -math.inf(f64))));
expect(math.isNan(tan64(math.nan(f64)))); expect(math.isNan(tan_(f64, math.nan(f64))));
} }

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/tanhf.c
// - sinh(+-inf) = +-1 // https://git.musl-libc.org/cgit/musl/tree/src/math/tanh.c
// - sinh(nan) = nan
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("../std.zig"); const std = @import("../std.zig");
@ -11,6 +11,12 @@ const expect = std.testing.expect;
const expo2 = @import("expo2.zig").expo2; const expo2 = @import("expo2.zig").expo2;
const maxInt = std.math.maxInt; 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) { pub fn tanh(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {

View File

@ -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 // https://git.musl-libc.org/cgit/musl/tree/src/math/truncf.c
// - trunc(+-inf) = +-inf // https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c
// - trunc(nan) = nan
const std = @import("../std.zig"); const std = @import("../std.zig");
const math = std.math; const math = std.math;
const expect = std.testing.expect; const expect = std.testing.expect;
const maxInt = std.math.maxInt; 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) { pub fn trunc(x: var) @typeOf(x) {
const T = @typeOf(x); const T = @typeOf(x);
return switch (T) { return switch (T) {