structstd.math[src]

Types

Type FunctionFrexp[src]

Parameters

T: type

Fields

significand: T
exponent: i32

Source Code

Source code
pub fn Frexp(comptime T: type) type {
    return struct {
        significand: T,
        exponent: i32,
    };
}

Type FunctionModf[src]

Parameters

T: type

Fields

fpart: T
ipart: T

Source Code

Source code
pub fn Modf(comptime T: type) type {
    return struct {
        fpart: T,
        ipart: T,
    };
}

Type FunctionComplex[src]

A complex number consisting of a real an imaginary part. T must be a floating-point value.

Parameters

T: type

Fields

re: T

Real part.

im: T

Imaginary part.

Functions

Functioninit[src]

pub fn init(re: T, im: T) Self

Create a new Complex number from the given real and imaginary parts.

Parameters

re: T
im: T

Source Code

Source code
pub fn init(re: T, im: T) Self {
    return Self{
        .re = re,
        .im = im,
    };
}

Functionadd[src]

pub fn add(self: Self, other: Self) Self

Returns the sum of two complex numbers.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn add(self: Self, other: Self) Self {
    return Self{
        .re = self.re + other.re,
        .im = self.im + other.im,
    };
}

Functionsub[src]

pub fn sub(self: Self, other: Self) Self

Returns the subtraction of two complex numbers.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn sub(self: Self, other: Self) Self {
    return Self{
        .re = self.re - other.re,
        .im = self.im - other.im,
    };
}

Functionmul[src]

pub fn mul(self: Self, other: Self) Self

Returns the product of two complex numbers.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn mul(self: Self, other: Self) Self {
    return Self{
        .re = self.re * other.re - self.im * other.im,
        .im = self.im * other.re + self.re * other.im,
    };
}

Functiondiv[src]

pub fn div(self: Self, other: Self) Self

Returns the quotient of two complex numbers.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn div(self: Self, other: Self) Self {
    const re_num = self.re * other.re + self.im * other.im;
    const im_num = self.im * other.re - self.re * other.im;
    const den = other.re * other.re + other.im * other.im;

    return Self{
        .re = re_num / den,
        .im = im_num / den,
    };
}

Functionconjugate[src]

pub fn conjugate(self: Self) Self

Returns the complex conjugate of a number.

Parameters

self: Self

Source Code

Source code
pub fn conjugate(self: Self) Self {
    return Self{
        .re = self.re,
        .im = -self.im,
    };
}

Functionneg[src]

pub fn neg(self: Self) Self

Returns the negation of a complex number.

Parameters

self: Self

Source Code

Source code
pub fn neg(self: Self) Self {
    return Self{
        .re = -self.re,
        .im = -self.im,
    };
}

Functionmulbyi[src]

pub fn mulbyi(self: Self) Self

Returns the product of complex number and i=sqrt(-1)

Parameters

self: Self

Source Code

Source code
pub fn mulbyi(self: Self) Self {
    return Self{
        .re = -self.im,
        .im = self.re,
    };
}

Functionreciprocal[src]

pub fn reciprocal(self: Self) Self

Returns the reciprocal of a complex number.

Parameters

self: Self

Source Code

Source code
pub fn reciprocal(self: Self) Self {
    const m = self.re * self.re + self.im * self.im;
    return Self{
        .re = self.re / m,
        .im = -self.im / m,
    };
}

Functionmagnitude[src]

pub fn magnitude(self: Self) T

Returns the magnitude of a complex number.

Parameters

self: Self

Source Code

Source code
pub fn magnitude(self: Self) T {
    return @sqrt(self.re * self.re + self.im * self.im);
}

FunctionsquaredMagnitude[src]

pub fn squaredMagnitude(self: Self) T

Parameters

self: Self

Source Code

Source code
pub fn squaredMagnitude(self: Self) T {
    return self.re * self.re + self.im * self.im;
}

Source Code

Source code
pub fn Complex(comptime T: type) type {
    return struct {
        const Self = @This();

        /// Real part.
        re: T,

        /// Imaginary part.
        im: T,

        /// Create a new Complex number from the given real and imaginary parts.
        pub fn init(re: T, im: T) Self {
            return Self{
                .re = re,
                .im = im,
            };
        }

        /// Returns the sum of two complex numbers.
        pub fn add(self: Self, other: Self) Self {
            return Self{
                .re = self.re + other.re,
                .im = self.im + other.im,
            };
        }

        /// Returns the subtraction of two complex numbers.
        pub fn sub(self: Self, other: Self) Self {
            return Self{
                .re = self.re - other.re,
                .im = self.im - other.im,
            };
        }

        /// Returns the product of two complex numbers.
        pub fn mul(self: Self, other: Self) Self {
            return Self{
                .re = self.re * other.re - self.im * other.im,
                .im = self.im * other.re + self.re * other.im,
            };
        }

        /// Returns the quotient of two complex numbers.
        pub fn div(self: Self, other: Self) Self {
            const re_num = self.re * other.re + self.im * other.im;
            const im_num = self.im * other.re - self.re * other.im;
            const den = other.re * other.re + other.im * other.im;

            return Self{
                .re = re_num / den,
                .im = im_num / den,
            };
        }

        /// Returns the complex conjugate of a number.
        pub fn conjugate(self: Self) Self {
            return Self{
                .re = self.re,
                .im = -self.im,
            };
        }

        /// Returns the negation of a complex number.
        pub fn neg(self: Self) Self {
            return Self{
                .re = -self.re,
                .im = -self.im,
            };
        }

        /// Returns the product of complex number and i=sqrt(-1)
        pub fn mulbyi(self: Self) Self {
            return Self{
                .re = -self.im,
                .im = self.re,
            };
        }

        /// Returns the reciprocal of a complex number.
        pub fn reciprocal(self: Self) Self {
            const m = self.re * self.re + self.im * self.im;
            return Self{
                .re = self.re / m,
                .im = -self.im / m,
            };
        }

        /// Returns the magnitude of a complex number.
        pub fn magnitude(self: Self) T {
            return @sqrt(self.re * self.re + self.im * self.im);
        }

        pub fn squaredMagnitude(self: Self) T {
            return self.re * self.re + self.im * self.im;
        }
    };
}

Type FunctionMin[src]

Given two types, returns the smallest one which is capable of holding the full range of the minimum value.

Parameters

A: type
B: type

Source Code

Source code
pub fn Min(comptime A: type, comptime B: type) type {
    switch (@typeInfo(A)) {
        .int => |a_info| switch (@typeInfo(B)) {
            .int => |b_info| if (a_info.signedness == .unsigned and b_info.signedness == .unsigned) {
                if (a_info.bits < b_info.bits) {
                    return A;
                } else {
                    return B;
                }
            },
            else => {},
        },
        else => {},
    }
    return @TypeOf(@as(A, 0) + @as(B, 0));
}

Type FunctionLog2Int[src]

Returns an unsigned int type that can hold the number of bits in T - 1. Suitable for 0-based bit indices of T.

Parameters

T: type

Source Code

Source code
pub fn Log2Int(comptime T: type) type {
    // comptime ceil log2
    if (T == comptime_int) return comptime_int;
    const bits: u16 = @typeInfo(T).int.bits;
    const log2_bits = 16 - @clz(bits - 1);
    return std.meta.Int(.unsigned, log2_bits);
}

Type FunctionLog2IntCeil[src]

Returns an unsigned int type that can hold the number of bits in T.

Parameters

T: type

Source Code

Source code
pub fn Log2IntCeil(comptime T: type) type {
    // comptime ceil log2
    if (T == comptime_int) return comptime_int;
    const bits: u16 = @typeInfo(T).int.bits;
    const log2_bits = 16 - @clz(bits);
    return std.meta.Int(.unsigned, log2_bits);
}

Type FunctionIntFittingRange[src]

Returns the smallest integer type that can hold both from and to.

Parameters

from: comptime_int
to: comptime_int

Example Usage

test IntFittingRange {
    try testing.expect(IntFittingRange(0, 0) == u0);
    try testing.expect(IntFittingRange(0, 1) == u1);
    try testing.expect(IntFittingRange(0, 2) == u2);
    try testing.expect(IntFittingRange(0, 3) == u2);
    try testing.expect(IntFittingRange(0, 4) == u3);
    try testing.expect(IntFittingRange(0, 7) == u3);
    try testing.expect(IntFittingRange(0, 8) == u4);
    try testing.expect(IntFittingRange(0, 9) == u4);
    try testing.expect(IntFittingRange(0, 15) == u4);
    try testing.expect(IntFittingRange(0, 16) == u5);
    try testing.expect(IntFittingRange(0, 17) == u5);
    try testing.expect(IntFittingRange(0, 4095) == u12);
    try testing.expect(IntFittingRange(2000, 4095) == u12);
    try testing.expect(IntFittingRange(0, 4096) == u13);
    try testing.expect(IntFittingRange(2000, 4096) == u13);
    try testing.expect(IntFittingRange(0, 4097) == u13);
    try testing.expect(IntFittingRange(2000, 4097) == u13);
    try testing.expect(IntFittingRange(0, 123456789123456798123456789) == u87);
    try testing.expect(IntFittingRange(0, 123456789123456798123456789123456789123456798123456789) == u177);

    try testing.expect(IntFittingRange(-1, -1) == i1);
    try testing.expect(IntFittingRange(-1, 0) == i1);
    try testing.expect(IntFittingRange(-1, 1) == i2);
    try testing.expect(IntFittingRange(-2, -2) == i2);
    try testing.expect(IntFittingRange(-2, -1) == i2);
    try testing.expect(IntFittingRange(-2, 0) == i2);
    try testing.expect(IntFittingRange(-2, 1) == i2);
    try testing.expect(IntFittingRange(-2, 2) == i3);
    try testing.expect(IntFittingRange(-1, 2) == i3);
    try testing.expect(IntFittingRange(-1, 3) == i3);
    try testing.expect(IntFittingRange(-1, 4) == i4);
    try testing.expect(IntFittingRange(-1, 7) == i4);
    try testing.expect(IntFittingRange(-1, 8) == i5);
    try testing.expect(IntFittingRange(-1, 9) == i5);
    try testing.expect(IntFittingRange(-1, 15) == i5);
    try testing.expect(IntFittingRange(-1, 16) == i6);
    try testing.expect(IntFittingRange(-1, 17) == i6);
    try testing.expect(IntFittingRange(-1, 4095) == i13);
    try testing.expect(IntFittingRange(-4096, 4095) == i13);
    try testing.expect(IntFittingRange(-1, 4096) == i14);
    try testing.expect(IntFittingRange(-4097, 4095) == i14);
    try testing.expect(IntFittingRange(-1, 4097) == i14);
    try testing.expect(IntFittingRange(-1, 123456789123456798123456789) == i88);
    try testing.expect(IntFittingRange(-1, 123456789123456798123456789123456789123456798123456789) == i178);
}

Source Code

Source code
pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) type {
    assert(from <= to);
    if (from == 0 and to == 0) {
        return u0;
    }
    const signedness: std.builtin.Signedness = if (from < 0) .signed else .unsigned;
    const largest_positive_integer = @max(if (from < 0) (-from) - 1 else from, to); // two's complement
    const base = log2(largest_positive_integer);
    const upper = (1 << base) - 1;
    var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1;
    if (signedness == .signed) {
        magnitude_bits += 1;
    }
    return std.meta.Int(signedness, magnitude_bits);
}

Type FunctionByteAlignedInt[src]

Aligns the given integer type bit width to a width divisible by 8.

Parameters

T: type

Example Usage

test ByteAlignedInt {
    try testing.expect(ByteAlignedInt(u0) == u0);
    try testing.expect(ByteAlignedInt(i0) == i0);
    try testing.expect(ByteAlignedInt(u3) == u8);
    try testing.expect(ByteAlignedInt(u8) == u8);
    try testing.expect(ByteAlignedInt(i111) == i112);
    try testing.expect(ByteAlignedInt(u129) == u136);
}

Source Code

Source code
pub fn ByteAlignedInt(comptime T: type) type {
    const info = @typeInfo(T).int;
    const bits = (info.bits + 7) / 8 * 8;
    const extended_type = std.meta.Int(info.signedness, bits);
    return extended_type;
}

Values

Constante[src]

Euler's number (e)

Source Code

Source code
pub const e = 2.71828182845904523536028747135266249775724709369995

Constantpi[src]

Archimedes' constant (π)

Source Code

Source code
pub const pi = 3.14159265358979323846264338327950288419716939937510

Constantphi[src]

Phi or Golden ratio constant (Φ) = (1 + sqrt(5))/2

Source Code

Source code
pub const phi = 1.6180339887498948482045868343656381177203091798057628621

Constanttau[src]

Circle constant (τ)

Source Code

Source code
pub const tau = 2 * pi

Constantlog2e[src]

log2(e)

Source Code

Source code
pub const log2e = 1.442695040888963407359924681001892137

Constantlog10e[src]

log10(e)

Source Code

Source code
pub const log10e = 0.434294481903251827651128918916605082

Constantln2[src]

ln(2)

Source Code

Source code
pub const ln2 = 0.693147180559945309417232121458176568

Constantln10[src]

ln(10)

Source Code

Source code
pub const ln10 = 2.302585092994045684017991454684364208

Constanttwo_sqrtpi[src]

2/sqrt(π)

Source Code

Source code
pub const two_sqrtpi = 1.128379167095512573896158903121545172

Constantsqrt2[src]

sqrt(2)

Source Code

Source code
pub const sqrt2 = 1.414213562373095048801688724209698079

Constantsqrt1_2[src]

1/sqrt(2)

Source Code

Source code
pub const sqrt1_2 = 0.707106781186547524400844362104849039

Constantrad_per_deg[src]

pi/180.0

Source Code

Source code
pub const rad_per_deg = 0.0174532925199432957692369076848861271344287188854172545609719144

Constantdeg_per_rad[src]

180.0/pi

Source Code

Source code
pub const deg_per_rad = 57.295779513082320876798154814105170332405472466564321549160243861

Error Sets

Error SetAlignCastError[src]

Errors

anyerror means the error set is known only at runtime.

UnalignedMemory

Source Code

Source code
pub const AlignCastError = error{UnalignedMemory}

Functions

FunctionfloatExponentBits[src]

pub inline fn floatExponentBits(comptime T: type) comptime_int

Returns the number of bits in the exponent of floating point type T.

Parameters

T: type

Source Code

Source code
pub inline fn floatExponentBits(comptime T: type) comptime_int {
    comptime assert(@typeInfo(T) == .float);

    return switch (@typeInfo(T).float.bits) {
        16 => 5,
        32 => 8,
        64 => 11,
        80 => 15,
        128 => 15,
        else => @compileError("unknown floating point type " ++ @typeName(T)),
    };
}

FunctionfloatMantissaBits[src]

pub inline fn floatMantissaBits(comptime T: type) comptime_int

Returns the number of bits in the mantissa of floating point type T.

Parameters

T: type

Source Code

Source code
pub inline fn floatMantissaBits(comptime T: type) comptime_int {
    comptime assert(@typeInfo(T) == .float);

    return switch (@typeInfo(T).float.bits) {
        16 => 10,
        32 => 23,
        64 => 52,
        80 => 64,
        128 => 112,
        else => @compileError("unknown floating point type " ++ @typeName(T)),
    };
}

FunctionfloatFractionalBits[src]

pub inline fn floatFractionalBits(comptime T: type) comptime_int

Returns the number of fractional bits in the mantissa of floating point type T.

Parameters

T: type

Source Code

Source code
pub inline fn floatFractionalBits(comptime T: type) comptime_int {
    comptime assert(@typeInfo(T) == .float);

    // standard IEEE floats have an implicit 0.m or 1.m integer part
    // f80 is special and has an explicitly stored bit in the MSB
    // this function corresponds to `MANT_DIG - 1' from C
    return switch (@typeInfo(T).float.bits) {
        16 => 10,
        32 => 23,
        64 => 52,
        80 => 63,
        128 => 112,
        else => @compileError("unknown floating point type " ++ @typeName(T)),
    };
}

FunctionfloatExponentMin[src]

pub inline fn floatExponentMin(comptime T: type) comptime_int

Returns the minimum exponent that can represent a normalised value in floating point type T.

Parameters

T: type

Source Code

Source code
pub inline fn floatExponentMin(comptime T: type) comptime_int {
    return -floatExponentMax(T) + 1;
}

FunctionfloatExponentMax[src]

pub inline fn floatExponentMax(comptime T: type) comptime_int

Returns the maximum exponent that can represent a normalised value in floating point type T.

Parameters

T: type

Source Code

Source code
pub inline fn floatExponentMax(comptime T: type) comptime_int {
    return (1 << (floatExponentBits(T) - 1)) - 1;
}

FunctionfloatTrueMin[src]

pub inline fn floatTrueMin(comptime T: type) T

Returns the smallest subnormal number representable in floating point type T.

Parameters

T: type

Source Code

Source code
pub inline fn floatTrueMin(comptime T: type) T {
    return reconstructFloat(T, floatExponentMin(T) - 1, 1);
}

FunctionfloatMin[src]

pub inline fn floatMin(comptime T: type) T

Returns the smallest normal number representable in floating point type T.

Parameters

T: type

Source Code

Source code
pub inline fn floatMin(comptime T: type) T {
    return reconstructFloat(T, floatExponentMin(T), mantissaOne(T));
}

FunctionfloatMax[src]

pub inline fn floatMax(comptime T: type) T

Returns the largest normal number representable in floating point type T.

Parameters

T: type

Source Code

Source code
pub inline fn floatMax(comptime T: type) T {
    const all1s_mantissa = (1 << floatMantissaBits(T)) - 1;
    return reconstructFloat(T, floatExponentMax(T), all1s_mantissa);
}

FunctionfloatEps[src]

pub inline fn floatEps(comptime T: type) T

Returns the machine epsilon of floating point type T.

Parameters

T: type

Source Code

Source code
pub inline fn floatEps(comptime T: type) T {
    return reconstructFloat(T, -floatFractionalBits(T), mantissaOne(T));
}

FunctionfloatEpsAt[src]

pub inline fn floatEpsAt(comptime T: type, x: T) T

Returns the local epsilon of floating point type T.

Parameters

T: type
x: T

Source Code

Source code
pub inline fn floatEpsAt(comptime T: type, x: T) T {
    switch (@typeInfo(T)) {
        .float => |F| {
            const U: type = @Type(.{ .int = .{ .signedness = .unsigned, .bits = F.bits } });
            const u: U = @bitCast(x);
            const y: T = @bitCast(u ^ 1);
            return @abs(x - y);
        },
        else => @compileError("floatEpsAt only supports floats"),
    }
}

Functioninf[src]

pub inline fn inf(comptime T: type) T

Returns the value inf for floating point type T.

Parameters

T: type

Example Usage

test inf {
    const inf_u16: u16 = 0x7C00;
    const inf_u32: u32 = 0x7F800000;
    const inf_u64: u64 = 0x7FF0000000000000;
    const inf_u80: u80 = 0x7FFF8000000000000000;
    const inf_u128: u128 = 0x7FFF0000000000000000000000000000;
    try expectEqual(inf_u16, @as(u16, @bitCast(inf(f16))));
    try expectEqual(inf_u32, @as(u32, @bitCast(inf(f32))));
    try expectEqual(inf_u64, @as(u64, @bitCast(inf(f64))));
    try expectEqual(inf_u80, @as(u80, @bitCast(inf(f80))));
    try expectEqual(inf_u128, @as(u128, @bitCast(inf(f128))));
}

Source Code

Source code
pub inline fn inf(comptime T: type) T {
    return reconstructFloat(T, floatExponentMax(T) + 1, mantissaOne(T));
}

Functionnan[src]

pub inline fn nan(comptime T: type) T

Returns the canonical quiet NaN representation for floating point type T.

Parameters

T: type

Example Usage

test nan {
    const qnan_u16: u16 = 0x7E00;
    const qnan_u32: u32 = 0x7FC00000;
    const qnan_u64: u64 = 0x7FF8000000000000;
    const qnan_u80: u80 = 0x7FFFC000000000000000;
    const qnan_u128: u128 = 0x7FFF8000000000000000000000000000;
    try expectEqual(qnan_u16, @as(u16, @bitCast(nan(f16))));
    try expectEqual(qnan_u32, @as(u32, @bitCast(nan(f32))));
    try expectEqual(qnan_u64, @as(u64, @bitCast(nan(f64))));
    try expectEqual(qnan_u80, @as(u80, @bitCast(nan(f80))));
    try expectEqual(qnan_u128, @as(u128, @bitCast(nan(f128))));
}

Source Code

Source code
pub inline fn nan(comptime T: type) T {
    return reconstructFloat(
        T,
        floatExponentMax(T) + 1,
        mantissaOne(T) | 1 << (floatFractionalBits(T) - 1),
    );
}

Functionsnan[src]

pub inline fn snan(comptime T: type) T

Returns a signalling NaN representation for floating point type T.

TODO: LLVM is known to miscompile on some architectures to quiet NaN - this is tracked by https://github.com/ziglang/zig/issues/14366

Parameters

T: type

Example Usage

test snan {
    const snan_u16: u16 = 0x7D00;
    const snan_u32: u32 = 0x7FA00000;
    const snan_u64: u64 = 0x7FF4000000000000;
    const snan_u80: u80 = 0x7FFFA000000000000000;
    const snan_u128: u128 = 0x7FFF4000000000000000000000000000;
    try expectEqual(snan_u16, @as(u16, @bitCast(snan(f16))));
    try expectEqual(snan_u32, @as(u32, @bitCast(snan(f32))));
    try expectEqual(snan_u64, @as(u64, @bitCast(snan(f64))));
    try expectEqual(snan_u80, @as(u80, @bitCast(snan(f80))));
    try expectEqual(snan_u128, @as(u128, @bitCast(snan(f128))));
}

Source Code

Source code
pub inline fn snan(comptime T: type) T {
    return reconstructFloat(
        T,
        floatExponentMax(T) + 1,
        mantissaOne(T) | 1 << (floatFractionalBits(T) - 2),
    );
}

FunctionapproxEqAbs[src]

pub fn approxEqAbs(comptime T: type, x: T, y: T, tolerance: T) bool

Performs an approximate comparison of two floating point values x and y. Returns true if the absolute difference between them is less or equal than the specified tolerance.

The tolerance parameter is the absolute tolerance used when determining if the two numbers are close enough; a good value for this parameter is a small multiple of floatEps(T).

Note that this function is recommended for comparing small numbers around zero; using approxEqRel is suggested otherwise.

NaN values are never considered equal to any value.

Parameters

T: type
x: T
y: T
tolerance: T

Example Usage

test approxEqAbs {
    inline for ([_]type{ f16, f32, f64, f128 }) |T| {
        const eps_value = comptime floatEps(T);
        const min_value = comptime floatMin(T);

        try testing.expect(approxEqAbs(T, 0.0, 0.0, eps_value));
        try testing.expect(approxEqAbs(T, -0.0, -0.0, eps_value));
        try testing.expect(approxEqAbs(T, 0.0, -0.0, eps_value));
        try testing.expect(!approxEqAbs(T, 1.0 + 2 * eps_value, 1.0, eps_value));
        try testing.expect(approxEqAbs(T, 1.0 + 1 * eps_value, 1.0, eps_value));
        try testing.expect(approxEqAbs(T, min_value, 0.0, eps_value * 2));
        try testing.expect(approxEqAbs(T, -min_value, 0.0, eps_value * 2));
    }

    comptime {
        // `comptime_float` is guaranteed to have the same precision and operations of
        // the largest other floating point type, which is f128 but it doesn't have a
        // defined layout so we can't rely on `@bitCast` to construct the smallest
        // possible epsilon value like we do in the tests above. In the same vein, we
        // also can't represent a max/min, `NaN` or `Inf` values.
        const eps_value = 1e-4;

        try testing.expect(approxEqAbs(comptime_float, 0.0, 0.0, eps_value));
        try testing.expect(approxEqAbs(comptime_float, -0.0, -0.0, eps_value));
        try testing.expect(approxEqAbs(comptime_float, 0.0, -0.0, eps_value));
        try testing.expect(!approxEqAbs(comptime_float, 1.0 + 2 * eps_value, 1.0, eps_value));
        try testing.expect(approxEqAbs(comptime_float, 1.0 + 1 * eps_value, 1.0, eps_value));
    }
}

Source Code

Source code
pub fn approxEqAbs(comptime T: type, x: T, y: T, tolerance: T) bool {
    assert(@typeInfo(T) == .float or @typeInfo(T) == .comptime_float);
    assert(tolerance >= 0);

    // Fast path for equal values (and signed zeros and infinites).
    if (x == y)
        return true;

    if (isNan(x) or isNan(y))
        return false;

    return @abs(x - y) <= tolerance;
}

FunctionapproxEqRel[src]

pub fn approxEqRel(comptime T: type, x: T, y: T, tolerance: T) bool

Performs an approximate comparison of two floating point values x and y. Returns true if the absolute difference between them is less or equal than max(|x|, |y|) * tolerance, where tolerance is a positive number greater than zero.

The tolerance parameter is the relative tolerance used when determining if the two numbers are close enough; a good value for this parameter is usually sqrt(floatEps(T)), meaning that the two numbers are considered equal if at least half of the digits are equal.

Note that for comparisons of small numbers around zero this function won't give meaningful results, use approxEqAbs instead.

NaN values are never considered equal to any value.

Parameters

T: type
x: T
y: T
tolerance: T

Example Usage

test approxEqRel {
    inline for ([_]type{ f16, f32, f64, f128 }) |T| {
        const eps_value = comptime floatEps(T);
        const sqrt_eps_value = comptime sqrt(eps_value);
        const nan_value = comptime nan(T);
        const inf_value = comptime inf(T);
        const min_value = comptime floatMin(T);

        try testing.expect(approxEqRel(T, 1.0, 1.0, sqrt_eps_value));
        try testing.expect(!approxEqRel(T, 1.0, 0.0, sqrt_eps_value));
        try testing.expect(!approxEqRel(T, 1.0, nan_value, sqrt_eps_value));
        try testing.expect(!approxEqRel(T, nan_value, nan_value, sqrt_eps_value));
        try testing.expect(approxEqRel(T, inf_value, inf_value, sqrt_eps_value));
        try testing.expect(approxEqRel(T, min_value, min_value, sqrt_eps_value));
        try testing.expect(approxEqRel(T, -min_value, -min_value, sqrt_eps_value));
    }

    comptime {
        // `comptime_float` is guaranteed to have the same precision and operations of
        // the largest other floating point type, which is f128 but it doesn't have a
        // defined layout so we can't rely on `@bitCast` to construct the smallest
        // possible epsilon value like we do in the tests above. In the same vein, we
        // also can't represent a max/min, `NaN` or `Inf` values.
        const eps_value = 1e-4;
        const sqrt_eps_value = sqrt(eps_value);

        try testing.expect(approxEqRel(comptime_float, 1.0, 1.0, sqrt_eps_value));
        try testing.expect(!approxEqRel(comptime_float, 1.0, 0.0, sqrt_eps_value));
    }
}

Source Code

Source code
pub fn approxEqRel(comptime T: type, x: T, y: T, tolerance: T) bool {
    assert(@typeInfo(T) == .float or @typeInfo(T) == .comptime_float);
    assert(tolerance > 0);

    // Fast path for equal values (and signed zeros and infinites).
    if (x == y)
        return true;

    if (isNan(x) or isNan(y))
        return false;

    return @abs(x - y) <= @max(@abs(x), @abs(y)) * tolerance;
}

FunctionraiseInvalid[src]

pub fn raiseInvalid() void

Source Code

Source code
pub fn raiseInvalid() void {
    // Raise INVALID fpu exception
}

FunctionraiseUnderflow[src]

pub fn raiseUnderflow() void

Source Code

Source code
pub fn raiseUnderflow() void {
    // Raise UNDERFLOW fpu exception
}

FunctionraiseOverflow[src]

pub fn raiseOverflow() void

Source Code

Source code
pub fn raiseOverflow() void {
    // Raise OVERFLOW fpu exception
}

FunctionraiseInexact[src]

pub fn raiseInexact() void

Source Code

Source code
pub fn raiseInexact() void {
    // Raise INEXACT fpu exception
}

FunctionraiseDivByZero[src]

pub fn raiseDivByZero() void

Source Code

Source code
pub fn raiseDivByZero() void {
    // Raise INEXACT fpu exception
}

FunctionisNan[src]

pub fn isNan(x: anytype) bool

Example Usage

test isNan {
    inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
        try expect(isNan(math.nan(T)));
        try expect(isNan(-math.nan(T)));
        try expect(isNan(math.snan(T)));
        try expect(!isNan(@as(T, 1.0)));
        try expect(!isNan(@as(T, math.inf(T))));
    }
}

Source Code

Source code
pub fn isNan(x: anytype) bool {
    return x != x;
}

FunctionisSignalNan[src]

pub fn isSignalNan(x: anytype) bool

TODO: LLVM is known to miscompile on some architectures to quiet NaN - this is tracked by https://github.com/ziglang/zig/issues/14366

Example Usage

test isSignalNan {
    inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
        // TODO: Signalling NaN values get converted to quiet NaN values in
        //       some cases where they shouldn't such that this can fail.
        //       See https://github.com/ziglang/zig/issues/14366
        if (!builtin.cpu.arch.isArm() and
            !builtin.cpu.arch.isAARCH64() and
            !builtin.cpu.arch.isMIPS32() and
            !builtin.cpu.arch.isPowerPC() and
            builtin.zig_backend != .stage2_c)
        {
            try expect(isSignalNan(math.snan(T)));
        }
        try expect(!isSignalNan(math.nan(T)));
        try expect(!isSignalNan(@as(T, 1.0)));
        try expect(!isSignalNan(math.inf(T)));
    }
}

Source Code

Source code
pub fn isSignalNan(x: anytype) bool {
    const T = @TypeOf(x);
    const U = meta.Int(.unsigned, @bitSizeOf(T));
    const quiet_signal_bit_mask = 1 << (math.floatFractionalBits(T) - 1);
    return isNan(x) and (@as(U, @bitCast(x)) & quiet_signal_bit_mask == 0);
}

Functionfrexp[src]

pub fn frexp(x: anytype) Frexp(@TypeOf(x))

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

Example Usage

test frexp {
    inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
        const max_exponent = math.floatExponentMax(T) + 1;
        const min_exponent = math.floatExponentMin(T) + 1;
        const truemin_exponent = min_exponent - math.floatFractionalBits(T);

        var result: Frexp(T) = undefined;
        comptime var x: T = undefined;

        // basic usage
        // value -> {significand, exponent},
        // value == significand * (2 ^ exponent)
        x = 1234.5678;
        result = frexp(x);
        try expectEqual(11, result.exponent);
        try expectApproxEqAbs(0.602816, result.significand, 1e-6);
        try expectEqual(x, math.ldexp(result.significand, result.exponent));

        // float maximum
        x = math.floatMax(T);
        result = frexp(x);
        try expectEqual(max_exponent, result.exponent);
        try expectEqual(1.0 - math.floatEps(T) / 2, result.significand);
        try expectEqual(x, math.ldexp(result.significand, result.exponent));

        // float minimum
        x = math.floatMin(T);
        result = frexp(x);
        try expectEqual(min_exponent, result.exponent);
        try expectEqual(0.5, result.significand);
        try expectEqual(x, math.ldexp(result.significand, result.exponent));

        // float true minimum
        // subnormal -> {normal, exponent}
        x = math.floatTrueMin(T);
        result = frexp(x);
        try expectEqual(truemin_exponent, result.exponent);
        try expectEqual(0.5, result.significand);
        try expectEqual(x, math.ldexp(result.significand, result.exponent));

        // infinity -> {infinity, zero} (+)
        result = frexp(math.inf(T));
        try expectEqual(0, result.exponent);
        try expect(math.isPositiveInf(result.significand));

        // infinity -> {infinity, zero} (-)
        result = frexp(-math.inf(T));
        try expectEqual(0, result.exponent);
        try expect(math.isNegativeInf(result.significand));

        // zero -> {zero, zero} (+)
        result = frexp(@as(T, 0.0));
        try expectEqual(0, result.exponent);
        try expect(math.isPositiveZero(result.significand));

        // zero -> {zero, zero} (-)
        result = frexp(@as(T, -0.0));
        try expectEqual(0, result.exponent);
        try expect(math.isNegativeZero(result.significand));

        // nan -> {nan, undefined}
        result = frexp(math.nan(T));
        try expect(math.isNan(result.significand));
    }
}

Source Code

Source code
pub fn frexp(x: anytype) Frexp(@TypeOf(x)) {
    const T: type = @TypeOf(x);

    const bits: comptime_int = @typeInfo(T).float.bits;
    const Int: type = std.meta.Int(.unsigned, bits);

    const exp_bits: comptime_int = math.floatExponentBits(T);
    const mant_bits: comptime_int = math.floatMantissaBits(T);
    const frac_bits: comptime_int = math.floatFractionalBits(T);
    const exp_min: comptime_int = math.floatExponentMin(T);

    const ExpInt: type = std.meta.Int(.unsigned, exp_bits);
    const MantInt: type = std.meta.Int(.unsigned, mant_bits);
    const FracInt: type = std.meta.Int(.unsigned, frac_bits);

    const unreal_exponent: comptime_int = (1 << exp_bits) - 1;
    const bias: comptime_int = (1 << (exp_bits - 1)) - 2;
    const exp_mask: comptime_int = unreal_exponent << mant_bits;
    const zero_exponent: comptime_int = bias << mant_bits;
    const sign_mask: comptime_int = 1 << (bits - 1);
    const not_exp: comptime_int = ~@as(Int, exp_mask);
    const ones_place: comptime_int = mant_bits - frac_bits;
    const extra_denorm_shift: comptime_int = 1 - ones_place;

    var result: Frexp(T) = undefined;
    var v: Int = @bitCast(x);

    const m: MantInt = @truncate(v);
    const e: ExpInt = @truncate(v >> mant_bits);

    switch (e) {
        0 => {
            if (m != 0) {
                // subnormal
                const offset = @clz(m);
                const shift = offset + extra_denorm_shift;

                v &= sign_mask;
                v |= zero_exponent;
                v |= math.shl(MantInt, m, shift);

                result.exponent = exp_min - @as(i32, offset) + ones_place;
            } else {
                // +-0 = (+-0, 0)
                result.exponent = 0;
            }
        },
        unreal_exponent => {
            // +-nan -> {+-nan, undefined}
            result.exponent = undefined;

            // +-inf -> {+-inf, 0}
            if (@as(FracInt, @truncate(v)) == 0)
                result.exponent = 0;
        },
        else => {
            // normal
            v &= not_exp;
            v |= zero_exponent;
            result.exponent = @as(i32, e) - bias;
        },
    }

    result.significand = @bitCast(v);
    return result;
}

Functionmodf[src]

pub fn modf(x: anytype) Modf(@TypeOf(x))

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. In comptime, may be used with comptime_float

Special Cases:

  • modf(+-inf) = +-inf, nan
  • modf(nan) = nan, nan

Example Usage

test modf {
    inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
        const epsilon: comptime_float = @max(1e-6, math.floatEps(T));

        var r: Modf(T) = undefined;

        r = modf(@as(T, 1.0));
        try expectEqual(1.0, r.ipart);
        try expectEqual(0.0, r.fpart);

        r = modf(@as(T, 0.34682));
        try expectEqual(0.0, r.ipart);
        try expectApproxEqAbs(@as(T, 0.34682), r.fpart, epsilon);

        r = modf(@as(T, 2.54576));
        try expectEqual(2.0, r.ipart);
        try expectApproxEqAbs(0.54576, r.fpart, epsilon);

        r = modf(@as(T, 3.9782));
        try expectEqual(3.0, r.ipart);
        try expectApproxEqAbs(0.9782, r.fpart, epsilon);
    }
}

Source Code

Source code
pub fn modf(x: anytype) Modf(@TypeOf(x)) {
    const ipart = @trunc(x);
    return .{
        .ipart = ipart,
        .fpart = x - ipart,
    };
}

Functioncopysign[src]

pub fn copysign(magnitude: anytype, sign: @TypeOf(magnitude)) @TypeOf(magnitude)

Returns a value with the magnitude of magnitude and the sign of sign.

Parameters

sign: @TypeOf(magnitude)

Example Usage

test copysign {
    inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
        try expect(copysign(@as(T, 1.0), @as(T, 1.0)) == 1.0);
        try expect(copysign(@as(T, 2.0), @as(T, -2.0)) == -2.0);
        try expect(copysign(@as(T, -3.0), @as(T, 3.0)) == 3.0);
        try expect(copysign(@as(T, -4.0), @as(T, -4.0)) == -4.0);
        try expect(copysign(@as(T, 5.0), @as(T, -500.0)) == -5.0);
        try expect(copysign(math.inf(T), @as(T, -0.0)) == -math.inf(T));
        try expect(copysign(@as(T, 6.0), -math.nan(T)) == -6.0);
    }
}

Source Code

Source code
pub fn copysign(magnitude: anytype, sign: @TypeOf(magnitude)) @TypeOf(magnitude) {
    const T = @TypeOf(magnitude);
    const TBits = std.meta.Int(.unsigned, @typeInfo(T).float.bits);
    const sign_bit_mask = @as(TBits, 1) << (@bitSizeOf(T) - 1);
    const mag = @as(TBits, @bitCast(magnitude)) & ~sign_bit_mask;
    const sgn = @as(TBits, @bitCast(sign)) & sign_bit_mask;
    return @as(T, @bitCast(mag | sgn));
}

FunctionisFinite[src]

pub fn isFinite(x: anytype) bool

Returns whether x is a finite value.

Example Usage

test isFinite {
    inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
        // normals
        try expect(isFinite(@as(T, 1.0)));
        try expect(isFinite(-@as(T, 1.0)));

        // zero & subnormals
        try expect(isFinite(@as(T, 0.0)));
        try expect(isFinite(@as(T, -0.0)));
        try expect(isFinite(math.floatTrueMin(T)));

        // other float limits
        try expect(isFinite(math.floatMin(T)));
        try expect(isFinite(math.floatMax(T)));

        // inf & nan
        try expect(!isFinite(math.inf(T)));
        try expect(!isFinite(-math.inf(T)));
        try expect(!isFinite(math.nan(T)));
        try expect(!isFinite(-math.nan(T)));
    }
}

Source Code

Source code
pub fn isFinite(x: anytype) bool {
    const T = @TypeOf(x);
    const TBits = std.meta.Int(.unsigned, @typeInfo(T).float.bits);
    const remove_sign = ~@as(TBits, 0) >> 1;
    return @as(TBits, @bitCast(x)) & remove_sign < @as(TBits, @bitCast(math.inf(T)));
}

FunctionisInf[src]

pub inline fn isInf(x: anytype) bool

Returns whether x is an infinity, ignoring sign.

Example Usage

test isInf {
    inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
        try expect(!isInf(@as(T, 0.0)));
        try expect(!isInf(@as(T, -0.0)));
        try expect(isInf(math.inf(T)));
        try expect(isInf(-math.inf(T)));
        try expect(!isInf(math.nan(T)));
        try expect(!isInf(-math.nan(T)));
    }
}

Source Code

Source code
pub inline fn isInf(x: anytype) bool {
    const T = @TypeOf(x);
    const TBits = std.meta.Int(.unsigned, @typeInfo(T).float.bits);
    const remove_sign = ~@as(TBits, 0) >> 1;
    return @as(TBits, @bitCast(x)) & remove_sign == @as(TBits, @bitCast(math.inf(T)));
}

FunctionisPositiveInf[src]

pub inline fn isPositiveInf(x: anytype) bool

Returns whether x is an infinity with a positive sign.

Example Usage

test isPositiveInf {
    inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
        try expect(!isPositiveInf(@as(T, 0.0)));
        try expect(!isPositiveInf(@as(T, -0.0)));
        try expect(isPositiveInf(math.inf(T)));
        try expect(!isPositiveInf(-math.inf(T)));
        try expect(!isInf(math.nan(T)));
        try expect(!isInf(-math.nan(T)));
    }
}

Source Code

Source code
pub inline fn isPositiveInf(x: anytype) bool {
    return x == math.inf(@TypeOf(x));
}

FunctionisNegativeInf[src]

pub inline fn isNegativeInf(x: anytype) bool

Returns whether x is an infinity with a negative sign.

Example Usage

test isNegativeInf {
    inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
        try expect(!isNegativeInf(@as(T, 0.0)));
        try expect(!isNegativeInf(@as(T, -0.0)));
        try expect(!isNegativeInf(math.inf(T)));
        try expect(isNegativeInf(-math.inf(T)));
        try expect(!isInf(math.nan(T)));
        try expect(!isInf(-math.nan(T)));
    }
}

Source Code

Source code
pub inline fn isNegativeInf(x: anytype) bool {
    return x == -math.inf(@TypeOf(x));
}

FunctionisPositiveZero[src]

pub inline fn isPositiveZero(x: anytype) bool

Returns whether x is positive zero.

Example Usage

test isPositiveZero {
    inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
        try expect(isPositiveZero(@as(T, 0.0)));
        try expect(!isPositiveZero(@as(T, -0.0)));
        try expect(!isPositiveZero(math.floatMin(T)));
        try expect(!isPositiveZero(math.floatMax(T)));
        try expect(!isPositiveZero(math.inf(T)));
        try expect(!isPositiveZero(-math.inf(T)));
    }
}

Source Code

Source code
pub inline fn isPositiveZero(x: anytype) bool {
    const T = @TypeOf(x);
    const bit_count = @typeInfo(T).float.bits;
    const TBits = std.meta.Int(.unsigned, bit_count);
    return @as(TBits, @bitCast(x)) == @as(TBits, 0);
}

FunctionisNegativeZero[src]

pub inline fn isNegativeZero(x: anytype) bool

Returns whether x is negative zero.

Example Usage

test isNegativeZero {
    inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
        try expect(isNegativeZero(@as(T, -0.0)));
        try expect(!isNegativeZero(@as(T, 0.0)));
        try expect(!isNegativeZero(math.floatMin(T)));
        try expect(!isNegativeZero(math.floatMax(T)));
        try expect(!isNegativeZero(math.inf(T)));
        try expect(!isNegativeZero(-math.inf(T)));
    }
}

Source Code

Source code
pub inline fn isNegativeZero(x: anytype) bool {
    const T = @TypeOf(x);
    const bit_count = @typeInfo(T).float.bits;
    const TBits = std.meta.Int(.unsigned, bit_count);
    return @as(TBits, @bitCast(x)) == @as(TBits, 1) << (bit_count - 1);
}

FunctionisNormal[src]

pub fn isNormal(x: anytype) bool

Returns whether x is neither zero, subnormal, infinity, or NaN.

Example Usage

test isNormal {
    // TODO add `c_longdouble' when math.inf(T) supports it
    inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
        const TBits = std.meta.Int(.unsigned, @bitSizeOf(T));

        // normals
        try expect(isNormal(@as(T, 1.0)));
        try expect(isNormal(math.floatMin(T)));
        try expect(isNormal(math.floatMax(T)));

        // subnormals
        try expect(!isNormal(@as(T, -0.0)));
        try expect(!isNormal(@as(T, 0.0)));
        try expect(!isNormal(@as(T, math.floatTrueMin(T))));

        // largest subnormal
        try expect(!isNormal(@as(T, @bitCast(~(~@as(TBits, 0) << math.floatFractionalBits(T))))));

        // non-finite numbers
        try expect(!isNormal(-math.inf(T)));
        try expect(!isNormal(math.inf(T)));
        try expect(!isNormal(math.nan(T)));

        // overflow edge-case (described in implementation, also see #10133)
        try expect(!isNormal(@as(T, @bitCast(~@as(TBits, 0)))));
    }
}

Source Code

Source code
pub fn isNormal(x: anytype) bool {
    const T = @TypeOf(x);
    const TBits = std.meta.Int(.unsigned, @typeInfo(T).float.bits);

    const increment_exp = 1 << math.floatMantissaBits(T);
    const remove_sign = ~@as(TBits, 0) >> 1;

    // We add 1 to the exponent, and if it overflows to 0 or becomes 1,
    // then it was all zeroes (subnormal) or all ones (special, inf/nan).
    // The sign bit is removed because all ones would overflow into it.
    // For f80, even though it has an explicit integer part stored,
    // the exponent effectively takes priority if mismatching.
    const value = @as(TBits, @bitCast(x)) +% increment_exp;
    return value & remove_sign >= (increment_exp << 1);
}

FunctionnextAfter[src]

pub fn nextAfter(comptime T: type, x: T, y: T) T

Returns the next representable value after x in the direction of y.

Special cases:

  • If x == y, y is returned.
  • For floats, if either x or y is a NaN, a NaN is returned.
  • For floats, if x == 0.0 and @abs(y) > 0.0, the smallest subnormal number with the sign of y is returned.

Parameters

T: type
x: T
y: T

Source Code

Source code
pub fn nextAfter(comptime T: type, x: T, y: T) T {
    return switch (@typeInfo(T)) {
        .int, .comptime_int => nextAfterInt(T, x, y),
        .float => nextAfterFloat(T, x, y),
        else => @compileError("expected int or non-comptime float, found '" ++ @typeName(T) ++ "'"),
    };
}

Functionsignbit[src]

pub fn signbit(x: anytype) bool

Returns whether x is negative or negative 0.

Example Usage

test signbit {
    inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
        try expect(!signbit(@as(T, 0.0)));
        try expect(!signbit(@as(T, 1.0)));
        try expect(signbit(@as(T, -2.0)));
        try expect(signbit(@as(T, -0.0)));
        try expect(!signbit(math.inf(T)));
        try expect(signbit(-math.inf(T)));
        try expect(!signbit(math.nan(T)));
        try expect(signbit(-math.nan(T)));
    }
}

Source Code

Source code
pub fn signbit(x: anytype) bool {
    const T = @TypeOf(x);
    const TBits = std.meta.Int(.unsigned, @typeInfo(T).float.bits);
    return @as(TBits, @bitCast(x)) >> (@bitSizeOf(T) - 1) != 0;
}

Functionldexp[src]

pub fn ldexp(x: anytype, n: i32) @TypeOf(x)

Returns x * 2^n.

Parameters

n: i32

Example Usage

test ldexp {
    // subnormals
    try expect(ldexp(@as(f16, 0x1.1FFp14), -14 - 9 - 15) == math.floatTrueMin(f16));
    try expect(ldexp(@as(f32, 0x1.3FFFFFp-1), -126 - 22) == math.floatTrueMin(f32));
    try expect(ldexp(@as(f64, 0x1.7FFFFFFFFFFFFp-1), -1022 - 51) == math.floatTrueMin(f64));
    try expect(ldexp(@as(f80, 0x1.7FFFFFFFFFFFFFFEp-1), -16382 - 62) == math.floatTrueMin(f80));
    try expect(ldexp(@as(f128, 0x1.7FFFFFFFFFFFFFFFFFFFFFFFFFFFp-1), -16382 - 111) == math.floatTrueMin(f128));

    try expect(ldexp(math.floatMax(f32), -128 - 149) > 0.0);
    try expect(ldexp(math.floatMax(f32), -128 - 149 - 1) == 0.0);

    @setEvalBranchQuota(10_000);

    inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
        const fractional_bits = math.floatFractionalBits(T);

        const min_exponent = math.floatExponentMin(T);
        const max_exponent = math.floatExponentMax(T);
        const exponent_bias = max_exponent;

        // basic usage
        try expect(ldexp(@as(T, 1.5), 4) == 24.0);

        // normals -> subnormals
        try expect(math.isNormal(ldexp(@as(T, 1.0), min_exponent)));
        try expect(!math.isNormal(ldexp(@as(T, 1.0), min_exponent - 1)));

        // normals -> zero
        try expect(ldexp(@as(T, 1.0), min_exponent - fractional_bits) > 0.0);
        try expect(ldexp(@as(T, 1.0), min_exponent - fractional_bits - 1) == 0.0);

        // subnormals -> zero
        try expect(ldexp(math.floatTrueMin(T), 0) > 0.0);
        try expect(ldexp(math.floatTrueMin(T), -1) == 0.0);

        // Multiplications might flush the denormals to zero, esp. at
        // runtime, so we manually construct the constants here instead.
        const Z = std.meta.Int(.unsigned, @bitSizeOf(T));
        const EightTimesTrueMin = @as(T, @bitCast(@as(Z, 8)));
        const TwoTimesTrueMin = @as(T, @bitCast(@as(Z, 2)));

        // subnormals -> subnormals
        try expect(ldexp(math.floatTrueMin(T), 3) == EightTimesTrueMin);
        try expect(ldexp(EightTimesTrueMin, -2) == TwoTimesTrueMin);
        try expect(ldexp(EightTimesTrueMin, -3) == math.floatTrueMin(T));

        // subnormals -> normals (+)
        try expect(ldexp(math.floatTrueMin(T), fractional_bits) == math.floatMin(T));
        try expect(ldexp(math.floatTrueMin(T), fractional_bits - 1) == math.floatMin(T) * 0.5);

        // subnormals -> normals (-)
        try expect(ldexp(-math.floatTrueMin(T), fractional_bits) == -math.floatMin(T));
        try expect(ldexp(-math.floatTrueMin(T), fractional_bits - 1) == -math.floatMin(T) * 0.5);

        // subnormals -> float limits (+inf)
        try expect(math.isFinite(ldexp(math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits - 1)));
        try expect(ldexp(math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits) == math.inf(T));

        // subnormals -> float limits (-inf)
        try expect(math.isFinite(ldexp(-math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits - 1)));
        try expect(ldexp(-math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits) == -math.inf(T));

        // infinity -> infinity
        try expect(ldexp(math.inf(T), math.maxInt(i32)) == math.inf(T));
        try expect(ldexp(math.inf(T), math.minInt(i32)) == math.inf(T));
        try expect(ldexp(math.inf(T), max_exponent) == math.inf(T));
        try expect(ldexp(math.inf(T), min_exponent) == math.inf(T));
        try expect(ldexp(-math.inf(T), math.maxInt(i32)) == -math.inf(T));
        try expect(ldexp(-math.inf(T), math.minInt(i32)) == -math.inf(T));

        // extremely large n
        try expect(ldexp(math.floatMax(T), math.maxInt(i32)) == math.inf(T));
        try expect(ldexp(math.floatMax(T), -math.maxInt(i32)) == 0.0);
        try expect(ldexp(math.floatMax(T), math.minInt(i32)) == 0.0);
        try expect(ldexp(math.floatTrueMin(T), math.maxInt(i32)) == math.inf(T));
        try expect(ldexp(math.floatTrueMin(T), -math.maxInt(i32)) == 0.0);
        try expect(ldexp(math.floatTrueMin(T), math.minInt(i32)) == 0.0);
    }
}

Source Code

Source code
pub fn ldexp(x: anytype, n: i32) @TypeOf(x) {
    const T = @TypeOf(x);
    const TBits = std.meta.Int(.unsigned, @typeInfo(T).float.bits);

    const exponent_bits = math.floatExponentBits(T);
    const mantissa_bits = math.floatMantissaBits(T);
    const fractional_bits = math.floatFractionalBits(T);

    const max_biased_exponent = 2 * math.floatExponentMax(T);
    const mantissa_mask = @as(TBits, (1 << mantissa_bits) - 1);

    const repr = @as(TBits, @bitCast(x));
    const sign_bit = repr & (1 << (exponent_bits + mantissa_bits));

    if (math.isNan(x) or !math.isFinite(x))
        return x;

    var exponent: i32 = @as(i32, @intCast((repr << 1) >> (mantissa_bits + 1)));
    if (exponent == 0)
        exponent += (@as(i32, exponent_bits) + @intFromBool(T == f80)) - @clz(repr << 1);

    if (n >= 0) {
        if (n > max_biased_exponent - exponent) {
            // Overflow. Return +/- inf
            return @as(T, @bitCast(@as(TBits, @bitCast(math.inf(T))) | sign_bit));
        } else if (exponent + n <= 0) {
            // Result is subnormal
            return @as(T, @bitCast((repr << @as(Log2Int(TBits), @intCast(n))) | sign_bit));
        } else if (exponent <= 0) {
            // Result is normal, but needs shifting
            var result = @as(TBits, @intCast(n + exponent)) << mantissa_bits;
            result |= (repr << @as(Log2Int(TBits), @intCast(1 - exponent))) & mantissa_mask;
            return @as(T, @bitCast(result | sign_bit));
        }

        // Result needs no shifting
        return @as(T, @bitCast(repr + (@as(TBits, @intCast(n)) << mantissa_bits)));
    } else {
        if (n <= -exponent) {
            if (n < -(mantissa_bits + exponent))
                return @as(T, @bitCast(sign_bit)); // Severe underflow. Return +/- 0

            // Result underflowed, we need to shift and round
            const shift = @as(Log2Int(TBits), @intCast(@min(-n, -(exponent + n) + 1)));
            const exact_tie: bool = @ctz(repr) == shift - 1;
            var result = repr & mantissa_mask;

            if (T != f80) // Include integer bit
                result |= @as(TBits, @intFromBool(exponent > 0)) << fractional_bits;
            result = @as(TBits, @intCast((result >> (shift - 1))));

            // Round result, including round-to-even for exact ties
            result = ((result + 1) >> 1) & ~@as(TBits, @intFromBool(exact_tie));
            return @as(T, @bitCast(result | sign_bit));
        }

        // Result is exact, and needs no shifting
        return @as(T, @bitCast(repr - (@as(TBits, @intCast(-n)) << mantissa_bits)));
    }
}

Functionldexp[src]

pub fn ldexp(x: anytype, n: i32) @TypeOf(x)

Returns x * 2^n.

Parameters

n: i32

Example Usage

test ldexp {
    // subnormals
    try expect(ldexp(@as(f16, 0x1.1FFp14), -14 - 9 - 15) == math.floatTrueMin(f16));
    try expect(ldexp(@as(f32, 0x1.3FFFFFp-1), -126 - 22) == math.floatTrueMin(f32));
    try expect(ldexp(@as(f64, 0x1.7FFFFFFFFFFFFp-1), -1022 - 51) == math.floatTrueMin(f64));
    try expect(ldexp(@as(f80, 0x1.7FFFFFFFFFFFFFFEp-1), -16382 - 62) == math.floatTrueMin(f80));
    try expect(ldexp(@as(f128, 0x1.7FFFFFFFFFFFFFFFFFFFFFFFFFFFp-1), -16382 - 111) == math.floatTrueMin(f128));

    try expect(ldexp(math.floatMax(f32), -128 - 149) > 0.0);
    try expect(ldexp(math.floatMax(f32), -128 - 149 - 1) == 0.0);

    @setEvalBranchQuota(10_000);

    inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
        const fractional_bits = math.floatFractionalBits(T);

        const min_exponent = math.floatExponentMin(T);
        const max_exponent = math.floatExponentMax(T);
        const exponent_bias = max_exponent;

        // basic usage
        try expect(ldexp(@as(T, 1.5), 4) == 24.0);

        // normals -> subnormals
        try expect(math.isNormal(ldexp(@as(T, 1.0), min_exponent)));
        try expect(!math.isNormal(ldexp(@as(T, 1.0), min_exponent - 1)));

        // normals -> zero
        try expect(ldexp(@as(T, 1.0), min_exponent - fractional_bits) > 0.0);
        try expect(ldexp(@as(T, 1.0), min_exponent - fractional_bits - 1) == 0.0);

        // subnormals -> zero
        try expect(ldexp(math.floatTrueMin(T), 0) > 0.0);
        try expect(ldexp(math.floatTrueMin(T), -1) == 0.0);

        // Multiplications might flush the denormals to zero, esp. at
        // runtime, so we manually construct the constants here instead.
        const Z = std.meta.Int(.unsigned, @bitSizeOf(T));
        const EightTimesTrueMin = @as(T, @bitCast(@as(Z, 8)));
        const TwoTimesTrueMin = @as(T, @bitCast(@as(Z, 2)));

        // subnormals -> subnormals
        try expect(ldexp(math.floatTrueMin(T), 3) == EightTimesTrueMin);
        try expect(ldexp(EightTimesTrueMin, -2) == TwoTimesTrueMin);
        try expect(ldexp(EightTimesTrueMin, -3) == math.floatTrueMin(T));

        // subnormals -> normals (+)
        try expect(ldexp(math.floatTrueMin(T), fractional_bits) == math.floatMin(T));
        try expect(ldexp(math.floatTrueMin(T), fractional_bits - 1) == math.floatMin(T) * 0.5);

        // subnormals -> normals (-)
        try expect(ldexp(-math.floatTrueMin(T), fractional_bits) == -math.floatMin(T));
        try expect(ldexp(-math.floatTrueMin(T), fractional_bits - 1) == -math.floatMin(T) * 0.5);

        // subnormals -> float limits (+inf)
        try expect(math.isFinite(ldexp(math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits - 1)));
        try expect(ldexp(math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits) == math.inf(T));

        // subnormals -> float limits (-inf)
        try expect(math.isFinite(ldexp(-math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits - 1)));
        try expect(ldexp(-math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits) == -math.inf(T));

        // infinity -> infinity
        try expect(ldexp(math.inf(T), math.maxInt(i32)) == math.inf(T));
        try expect(ldexp(math.inf(T), math.minInt(i32)) == math.inf(T));
        try expect(ldexp(math.inf(T), max_exponent) == math.inf(T));
        try expect(ldexp(math.inf(T), min_exponent) == math.inf(T));
        try expect(ldexp(-math.inf(T), math.maxInt(i32)) == -math.inf(T));
        try expect(ldexp(-math.inf(T), math.minInt(i32)) == -math.inf(T));

        // extremely large n
        try expect(ldexp(math.floatMax(T), math.maxInt(i32)) == math.inf(T));
        try expect(ldexp(math.floatMax(T), -math.maxInt(i32)) == 0.0);
        try expect(ldexp(math.floatMax(T), math.minInt(i32)) == 0.0);
        try expect(ldexp(math.floatTrueMin(T), math.maxInt(i32)) == math.inf(T));
        try expect(ldexp(math.floatTrueMin(T), -math.maxInt(i32)) == 0.0);
        try expect(ldexp(math.floatTrueMin(T), math.minInt(i32)) == 0.0);
    }
}

Source Code

Source code
pub fn ldexp(x: anytype, n: i32) @TypeOf(x) {
    const T = @TypeOf(x);
    const TBits = std.meta.Int(.unsigned, @typeInfo(T).float.bits);

    const exponent_bits = math.floatExponentBits(T);
    const mantissa_bits = math.floatMantissaBits(T);
    const fractional_bits = math.floatFractionalBits(T);

    const max_biased_exponent = 2 * math.floatExponentMax(T);
    const mantissa_mask = @as(TBits, (1 << mantissa_bits) - 1);

    const repr = @as(TBits, @bitCast(x));
    const sign_bit = repr & (1 << (exponent_bits + mantissa_bits));

    if (math.isNan(x) or !math.isFinite(x))
        return x;

    var exponent: i32 = @as(i32, @intCast((repr << 1) >> (mantissa_bits + 1)));
    if (exponent == 0)
        exponent += (@as(i32, exponent_bits) + @intFromBool(T == f80)) - @clz(repr << 1);

    if (n >= 0) {
        if (n > max_biased_exponent - exponent) {
            // Overflow. Return +/- inf
            return @as(T, @bitCast(@as(TBits, @bitCast(math.inf(T))) | sign_bit));
        } else if (exponent + n <= 0) {
            // Result is subnormal
            return @as(T, @bitCast((repr << @as(Log2Int(TBits), @intCast(n))) | sign_bit));
        } else if (exponent <= 0) {
            // Result is normal, but needs shifting
            var result = @as(TBits, @intCast(n + exponent)) << mantissa_bits;
            result |= (repr << @as(Log2Int(TBits), @intCast(1 - exponent))) & mantissa_mask;
            return @as(T, @bitCast(result | sign_bit));
        }

        // Result needs no shifting
        return @as(T, @bitCast(repr + (@as(TBits, @intCast(n)) << mantissa_bits)));
    } else {
        if (n <= -exponent) {
            if (n < -(mantissa_bits + exponent))
                return @as(T, @bitCast(sign_bit)); // Severe underflow. Return +/- 0

            // Result underflowed, we need to shift and round
            const shift = @as(Log2Int(TBits), @intCast(@min(-n, -(exponent + n) + 1)));
            const exact_tie: bool = @ctz(repr) == shift - 1;
            var result = repr & mantissa_mask;

            if (T != f80) // Include integer bit
                result |= @as(TBits, @intFromBool(exponent > 0)) << fractional_bits;
            result = @as(TBits, @intCast((result >> (shift - 1))));

            // Round result, including round-to-even for exact ties
            result = ((result + 1) >> 1) & ~@as(TBits, @intFromBool(exact_tie));
            return @as(T, @bitCast(result | sign_bit));
        }

        // Result is exact, and needs no shifting
        return @as(T, @bitCast(repr - (@as(TBits, @intCast(-n)) << mantissa_bits)));
    }
}

Functionpow[src]

pub fn pow(comptime T: type, x: T, y: T) T

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

Parameters

T: type
x: T
y: T

Example Usage

test pow {
    const epsilon = 0.000001;

    try expect(math.approxEqAbs(f32, pow(f32, 0.0, 3.3), 0.0, epsilon));
    try expect(math.approxEqAbs(f32, pow(f32, 0.8923, 3.3), 0.686572, epsilon));
    try expect(math.approxEqAbs(f32, pow(f32, 0.2, 3.3), 0.004936, epsilon));
    try expect(math.approxEqAbs(f32, pow(f32, 1.5, 3.3), 3.811546, epsilon));
    try expect(math.approxEqAbs(f32, pow(f32, 37.45, 3.3), 155736.703125, epsilon));
    try expect(math.approxEqAbs(f32, pow(f32, 89.123, 3.3), 2722489.5, epsilon));

    try expect(math.approxEqAbs(f64, pow(f64, 0.0, 3.3), 0.0, epsilon));
    try expect(math.approxEqAbs(f64, pow(f64, 0.8923, 3.3), 0.686572, epsilon));
    try expect(math.approxEqAbs(f64, pow(f64, 0.2, 3.3), 0.004936, epsilon));
    try expect(math.approxEqAbs(f64, pow(f64, 1.5, 3.3), 3.811546, epsilon));
    try expect(math.approxEqAbs(f64, pow(f64, 37.45, 3.3), 155736.7160616, epsilon));
    try expect(math.approxEqAbs(f64, pow(f64, 89.123, 3.3), 2722490.231436, epsilon));
}

Source Code

Source code
pub fn pow(comptime T: type, x: T, y: T) T {
    if (@typeInfo(T) == .int) {
        return math.powi(T, x, y) catch unreachable;
    }

    if (T != f32 and T != f64) {
        @compileError("pow not implemented for " ++ @typeName(T));
    }

    // pow(x, +-0) = 1      for all x
    // pow(1, y) = 1        for all y
    if (y == 0 or x == 1) {
        return 1;
    }

    // pow(nan, y) = nan    for all y
    // pow(x, nan) = nan    for all x
    if (math.isNan(x) or math.isNan(y)) {
        @branchHint(.unlikely);
        return math.nan(T);
    }

    // pow(x, 1) = x        for all x
    if (y == 1) {
        return x;
    }

    if (x == 0) {
        if (y < 0) {
            // pow(+-0, y) = +- 0   for y an odd integer
            if (isOddInteger(y)) {
                return math.copysign(math.inf(T), x);
            }
            // pow(+-0, y) = +inf   for y an even integer
            else {
                return math.inf(T);
            }
        } else {
            if (isOddInteger(y)) {
                return x;
            } else {
                return 0;
            }
        }
    }

    if (math.isInf(y)) {
        // pow(-1, inf) = 1     for all x
        if (x == -1) {
            return 1.0;
        }
        // pow(x, +inf) = +0    for |x| < 1
        // pow(x, -inf) = +0    for |x| > 1
        else if ((@abs(x) < 1) == math.isPositiveInf(y)) {
            return 0;
        }
        // pow(x, -inf) = +inf  for |x| < 1
        // pow(x, +inf) = +inf  for |x| > 1
        else {
            return math.inf(T);
        }
    }

    if (math.isInf(x)) {
        if (math.isNegativeInf(x)) {
            return pow(T, 1 / x, -y);
        }
        // pow(+inf, y) = +0    for y < 0
        else if (y < 0) {
            return 0;
        }
        // pow(+inf, y) = +0    for y > 0
        else if (y > 0) {
            return math.inf(T);
        }
    }

    // special case sqrt
    if (y == 0.5) {
        return @sqrt(x);
    }

    if (y == -0.5) {
        return 1 / @sqrt(x);
    }

    const r1 = math.modf(@abs(y));
    var yi = r1.ipart;
    var yf = r1.fpart;

    if (yf != 0 and x < 0) {
        return math.nan(T);
    }
    if (yi >= 1 << (@typeInfo(T).float.bits - 1)) {
        return @exp(y * @log(x));
    }

    // a = a1 * 2^ae
    var a1: T = 1.0;
    var ae: i32 = 0;

    // a *= x^yf
    if (yf != 0) {
        if (yf > 0.5) {
            yf -= 1;
            yi += 1;
        }
        a1 = @exp(yf * @log(x));
    }

    // a *= x^yi
    const r2 = math.frexp(x);
    var xe = r2.exponent;
    var x1 = r2.significand;

    var i = @as(std.meta.Int(.signed, @typeInfo(T).float.bits), @intFromFloat(yi));
    while (i != 0) : (i >>= 1) {
        const overflow_shift = math.floatExponentBits(T) + 1;
        if (xe < -(1 << overflow_shift) or (1 << overflow_shift) < xe) {
            // catch xe before it overflows the left shift below
            // Since i != 0 it has at least one bit still set, so ae will accumulate xe
            // on at least one more iteration, ae += xe is a lower bound on ae
            // the lower bound on ae exceeds the size of a float exp
            // so the final call to Ldexp will produce under/overflow (0/Inf)
            ae += xe;
            break;
        }
        if (i & 1 == 1) {
            a1 *= x1;
            ae += xe;
        }
        x1 *= x1;
        xe <<= 1;
        if (x1 < 0.5) {
            x1 += x1;
            xe -= 1;
        }
    }

    // a *= a1 * 2^ae
    if (y < 0) {
        a1 = 1 / a1;
        ae = -ae;
    }

    return math.scalbn(a1, ae);
}

Functionpowi[src]

pub fn powi(comptime T: type, x: T, y: T) (error{ Overflow, Underflow, }!T)

Returns the power of x raised by the integer y (x^y).

Errors:

  • Overflow: Integer overflow or Infinity
  • Underflow: Absolute value of result smaller than 1 Edge case rules ordered by precedence:
  • powi(T, x, 0) = 1 unless T is i1, i0, u0
  • powi(T, 0, x) = 0 when x > 0
  • powi(T, 0, x) = Overflow
  • powi(T, 1, y) = 1
  • powi(T, -1, y) = -1 for y an odd integer
  • powi(T, -1, y) = 1 unless T is i1, i0, u0
  • powi(T, -1, y) = Overflow
  • powi(T, x, y) = Overflow when y >= @bitSizeOf(x)
  • powi(T, x, y) = Underflow when y < 0

Parameters

T: type
x: T
y: T

Example Usage

test powi {
    try testing.expectError(error.Overflow, powi(i8, -66, 6));
    try testing.expectError(error.Overflow, powi(i16, -13, 13));
    try testing.expectError(error.Overflow, powi(i32, -32, 21));
    try testing.expectError(error.Overflow, powi(i64, -24, 61));
    try testing.expectError(error.Overflow, powi(i17, -15, 15));
    try testing.expectError(error.Overflow, powi(i42, -6, 40));

    try testing.expect((try powi(i8, -5, 3)) == -125);
    try testing.expect((try powi(i16, -16, 3)) == -4096);
    try testing.expect((try powi(i32, -91, 3)) == -753571);
    try testing.expect((try powi(i64, -36, 6)) == 2176782336);
    try testing.expect((try powi(i17, -2, 15)) == -32768);
    try testing.expect((try powi(i42, -5, 7)) == -78125);

    try testing.expect((try powi(u8, 6, 2)) == 36);
    try testing.expect((try powi(u16, 5, 4)) == 625);
    try testing.expect((try powi(u32, 12, 6)) == 2985984);
    try testing.expect((try powi(u64, 34, 2)) == 1156);
    try testing.expect((try powi(u17, 16, 3)) == 4096);
    try testing.expect((try powi(u42, 34, 6)) == 1544804416);

    try testing.expectError(error.Overflow, powi(i8, 120, 7));
    try testing.expectError(error.Overflow, powi(i16, 73, 15));
    try testing.expectError(error.Overflow, powi(i32, 23, 31));
    try testing.expectError(error.Overflow, powi(i64, 68, 61));
    try testing.expectError(error.Overflow, powi(i17, 15, 15));
    try testing.expectError(error.Overflow, powi(i42, 121312, 41));

    try testing.expectError(error.Overflow, powi(u8, 123, 7));
    try testing.expectError(error.Overflow, powi(u16, 2313, 15));
    try testing.expectError(error.Overflow, powi(u32, 8968, 31));
    try testing.expectError(error.Overflow, powi(u64, 2342, 63));
    try testing.expectError(error.Overflow, powi(u17, 2723, 16));
    try testing.expectError(error.Overflow, powi(u42, 8234, 41));

    const minInt = std.math.minInt;
    try testing.expect((try powi(i8, -2, 7)) == minInt(i8));
    try testing.expect((try powi(i16, -2, 15)) == minInt(i16));
    try testing.expect((try powi(i32, -2, 31)) == minInt(i32));
    try testing.expect((try powi(i64, -2, 63)) == minInt(i64));

    try testing.expectError(error.Underflow, powi(i8, 6, -2));
    try testing.expectError(error.Underflow, powi(i16, 5, -4));
    try testing.expectError(error.Underflow, powi(i32, 12, -6));
    try testing.expectError(error.Underflow, powi(i64, 34, -2));
    try testing.expectError(error.Underflow, powi(i17, 16, -3));
    try testing.expectError(error.Underflow, powi(i42, 34, -6));
}

Source Code

Source code
pub fn powi(comptime T: type, x: T, y: T) (error{
    Overflow,
    Underflow,
}!T) {
    const bit_size = @typeInfo(T).int.bits;

    // `y & 1 == 0` won't compile when `does_one_overflow`.
    const does_one_overflow = math.maxInt(T) < 1;
    const is_y_even = !does_one_overflow and y & 1 == 0;

    if (x == 1 or y == 0 or (x == -1 and is_y_even)) {
        if (does_one_overflow) {
            return error.Overflow;
        } else {
            return 1;
        }
    }

    if (x == -1) {
        return -1;
    }

    if (x == 0) {
        if (y > 0) {
            return 0;
        } else {
            // Infinity/NaN, not overflow in strict sense
            return error.Overflow;
        }
    }
    // x >= 2 or x <= -2 from this point
    if (y >= bit_size) {
        return error.Overflow;
    }
    if (y < 0) {
        return error.Underflow;
    }

    // invariant :
    // return value = powi(T, base, exp) * acc;

    var base = x;
    var exp = y;
    var acc: T = if (does_one_overflow) unreachable else 1;

    while (exp > 1) {
        if (exp & 1 == 1) {
            const ov = @mulWithOverflow(acc, base);
            if (ov[1] != 0) return error.Overflow;
            acc = ov[0];
        }

        exp >>= 1;

        const ov = @mulWithOverflow(base, base);
        if (ov[1] != 0) return error.Overflow;
        base = ov[0];
    }

    if (exp == 1) {
        const ov = @mulWithOverflow(acc, base);
        if (ov[1] != 0) return error.Overflow;
        acc = ov[0];
    }

    return acc;
}

Functionsqrt[src]

pub fn sqrt(x: anytype) Sqrt(@TypeOf(x))

Returns the square root of x.

Special Cases:

  • sqrt(+inf) = +inf
  • sqrt(+-0) = +-0
  • sqrt(x) = nan if x < 0
  • sqrt(nan) = nan TODO Decide if all this logic should be implemented directly in the @sqrt builtin function.

Source Code

Source code
pub fn sqrt(x: anytype) Sqrt(@TypeOf(x)) {
    const T = @TypeOf(x);
    switch (@typeInfo(T)) {
        .float, .comptime_float => return @sqrt(x),
        .comptime_int => comptime {
            if (x > maxInt(u128)) {
                @compileError("sqrt not implemented for comptime_int greater than 128 bits");
            }
            if (x < 0) {
                @compileError("sqrt on negative number");
            }
            return @as(T, sqrt_int(u128, x));
        },
        .int => |IntType| switch (IntType.signedness) {
            .signed => @compileError("sqrt not implemented for signed integers"),
            .unsigned => return sqrt_int(T, x),
        },
        else => @compileError("sqrt not implemented for " ++ @typeName(T)),
    }
}

Functioncbrt[src]

pub fn cbrt(x: anytype) @TypeOf(x)

Returns the cube root of x.

Special Cases:

  • cbrt(+-0) = +-0
  • cbrt(+-inf) = +-inf
  • cbrt(nan) = nan

Example Usage

test cbrt {
    try expect(cbrt(@as(f32, 0.0)) == cbrt32(0.0));
    try expect(cbrt(@as(f64, 0.0)) == cbrt64(0.0));
}

Source Code

Source code
pub fn cbrt(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    return switch (T) {
        f32 => cbrt32(x),
        f64 => cbrt64(x),
        else => @compileError("cbrt not implemented for " ++ @typeName(T)),
    };
}

Functionacos[src]

pub fn acos(x: anytype) @TypeOf(x)

Returns the arc-cosine of x.

Special cases:

  • acos(x) = nan if x < -1 or x > 1

Example Usage

test acos {
    try expect(acos(@as(f32, 0.0)) == acos32(0.0));
    try expect(acos(@as(f64, 0.0)) == acos64(0.0));
}

Source Code

Source code
pub fn acos(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    return switch (T) {
        f32 => acos32(x),
        f64 => acos64(x),
        else => @compileError("acos not implemented for " ++ @typeName(T)),
    };
}

Functionasin[src]

pub fn asin(x: anytype) @TypeOf(x)

Returns the arc-sin of x.

Special Cases:

  • asin(+-0) = +-0
  • asin(x) = nan if x < -1 or x > 1

Example Usage

test asin {
    try expect(asin(@as(f32, 0.0)) == asin32(0.0));
    try expect(asin(@as(f64, 0.0)) == asin64(0.0));
}

Source Code

Source code
pub fn asin(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    return switch (T) {
        f32 => asin32(x),
        f64 => asin64(x),
        else => @compileError("asin not implemented for " ++ @typeName(T)),
    };
}

Functionatan[src]

pub fn atan(x: anytype) @TypeOf(x)

Returns the arc-tangent of x.

Special Cases:

  • atan(+-0) = +-0
  • atan(+-inf) = +-pi/2

Example Usage

test atan {
    try expect(@as(u32, @bitCast(atan(@as(f32, 0.2)))) == @as(u32, @bitCast(atan32(0.2))));
    try expect(atan(@as(f64, 0.2)) == atan64(0.2));
}

Source Code

Source code
pub fn atan(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    return switch (T) {
        f32 => atan32(x),
        f64 => atan64(x),
        else => @compileError("atan not implemented for " ++ @typeName(T)),
    };
}

Functionatan2[src]

pub fn atan2(y: anytype, x: anytype) @TypeOf(x, y)

Returns the arc-tangent of y/x.

Special Cases:

y x radians
fin nan nan
nan fin nan
+0 >=+0 +0
-0 >=+0 -0
+0 <=-0 pi
-0 <=-0 -pi
pos 0 +pi/2
neg 0 -pi/2
+inf +inf +pi/4
-inf +inf -pi/4
+inf -inf 3pi/4
-inf -inf -3pi/4
fin +inf 0
pos -inf +pi
neg -inf -pi
+inf fin +pi/2
-inf fin -pi/2

Example Usage

test atan2 {
    const y32: f32 = 0.2;
    const x32: f32 = 0.21;
    const y64: f64 = 0.2;
    const x64: f64 = 0.21;
    try expect(atan2(y32, x32) == atan2_32(0.2, 0.21));
    try expect(atan2(y64, x64) == atan2_64(0.2, 0.21));
}

Source Code

Source code
pub fn atan2(y: anytype, x: anytype) @TypeOf(x, y) {
    const T = @TypeOf(x, y);
    return switch (T) {
        f32 => atan2_32(y, x),
        f64 => atan2_64(y, x),
        else => @compileError("atan2 not implemented for " ++ @typeName(T)),
    };
}

Functionhypot[src]

pub fn hypot(x: anytype, y: anytype) @TypeOf(x, y)

Returns sqrt(x * x + y * y), avoiding unnecessary overflow and underflow.

Special Cases:

x y hypot
+-inf any +inf
any +-inf +inf
nan fin nan
fin nan nan

Example Usage

test hypot {
    try expect(hypot(0.3, 0.4) == 0.5);
}

Source Code

Source code
pub fn hypot(x: anytype, y: anytype) @TypeOf(x, y) {
    const T = @TypeOf(x, y);
    switch (@typeInfo(T)) {
        .float => {},
        .comptime_float => return @sqrt(x * x + y * y),
        else => @compileError("hypot not implemented for " ++ @typeName(T)),
    }
    const lower = @sqrt(floatMin(T));
    const upper = @sqrt(floatMax(T) / 2);
    const incre = @sqrt(floatEps(T) / 2);
    const scale = floatEpsAt(T, incre);
    const hypfn = if (emulateFma(T)) hypotUnfused else hypotFused;
    var major: T = x;
    var minor: T = y;
    if (isInf(major) or isInf(minor)) return inf(T);
    if (isNan(major) or isNan(minor)) return nan(T);
    if (T == f16) return @floatCast(@sqrt(@mulAdd(f32, x, x, @as(f32, y) * y)));
    if (T == f32) return @floatCast(@sqrt(@mulAdd(f64, x, x, @as(f64, y) * y)));
    major = @abs(major);
    minor = @abs(minor);
    if (minor > major) {
        const tempo = major;
        major = minor;
        minor = tempo;
    }
    if (major * incre >= minor) return major;
    if (major > upper) return hypfn(T, major * scale, minor * scale) / scale;
    if (minor < lower) return hypfn(T, major / scale, minor / scale) * scale;
    return hypfn(T, major, minor);
}

Functionexpm1[src]

pub fn expm1(x: anytype) @TypeOf(x)

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

Example Usage

test expm1 {
    try expect(expm1(@as(f32, 0.0)) == expm1_32(0.0));
    try expect(expm1(@as(f64, 0.0)) == expm1_64(0.0));
}

Source Code

Source code
pub fn expm1(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    return switch (T) {
        f32 => expm1_32(x),
        f64 => expm1_64(x),
        else => @compileError("exp1m not implemented for " ++ @typeName(T)),
    };
}

Functionilogb[src]

pub fn ilogb(x: anytype) i32

Returns the binary exponent of x as an integer.

Special Cases:

  • ilogb(+-inf) = maxInt(i32)
  • ilogb(+-0) = minInt(i32)
  • ilogb(nan) = minInt(i32)

Source Code

Source code
pub fn ilogb(x: anytype) i32 {
    const T = @TypeOf(x);
    return ilogbX(T, x);
}

Functionlog[src]

pub fn log(comptime T: type, base: T, x: T) T

Returns the logarithm of x for the provided base.

Parameters

T: type
base: T
x: T

Source Code

Source code
pub fn log(comptime T: type, base: T, x: T) T {
    if (base == 2) {
        return math.log2(x);
    } else if (base == 10) {
        return math.log10(x);
    } else if ((@typeInfo(T) == .float or @typeInfo(T) == .comptime_float) and base == math.e) {
        return @log(x);
    }

    const float_base = math.lossyCast(f64, base);
    switch (@typeInfo(T)) {
        .comptime_float => {
            return @as(comptime_float, @log(@as(f64, x)) / @log(float_base));
        },

        .comptime_int => {
            return @as(comptime_int, math.log_int(comptime_int, base, x));
        },

        .int => |IntType| switch (IntType.signedness) {
            .signed => @compileError("log not implemented for signed integers"),
            .unsigned => return @as(T, math.log_int(T, base, x)),
        },

        .float => {
            switch (T) {
                f32 => return @as(f32, @floatCast(@log(@as(f64, x)) / @log(float_base))),
                f64 => return @log(x) / @log(float_base),
                else => @compileError("log not implemented for " ++ @typeName(T)),
            }
        },

        else => {
            @compileError("log expects integer or float, found '" ++ @typeName(T) ++ "'");
        },
    }
}

Functionlog2[src]

pub fn log2(x: anytype) @TypeOf(x)

Returns the base-2 logarithm of x.

Special Cases:

  • log2(+inf) = +inf
  • log2(0) = -inf
  • log2(x) = nan if x < 0
  • log2(nan) = nan

Example Usage

test log2 {
    // https://github.com/ziglang/zig/issues/13703
    if (builtin.cpu.arch == .aarch64 and builtin.os.tag == .windows) return error.SkipZigTest;

    try expect(log2(@as(f32, 0.2)) == @log2(0.2));
    try expect(log2(@as(f64, 0.2)) == @log2(0.2));
    comptime {
        try expect(log2(1) == 0);
        try expect(log2(15) == 3);
        try expect(log2(16) == 4);
        try expect(log2(1 << 4073) == 4073);
    }
}

Source Code

Source code
pub fn log2(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    switch (@typeInfo(T)) {
        .comptime_float => {
            return @as(comptime_float, @log2(x));
        },
        .float => return @log2(x),
        .comptime_int => comptime {
            var x_shifted = x;
            // First, calculate floorPowerOfTwo(x)
            var shift_amt = 1;
            while (x_shifted >> (shift_amt << 1) != 0) shift_amt <<= 1;

            // Answer is in the range [shift_amt, 2 * shift_amt - 1]
            // We can find it in O(log(N)) using binary search.
            var result = 0;
            while (shift_amt != 0) : (shift_amt >>= 1) {
                if (x_shifted >> shift_amt != 0) {
                    x_shifted >>= shift_amt;
                    result += shift_amt;
                }
            }
            return result;
        },
        .int => |IntType| switch (IntType.signedness) {
            .signed => @compileError("log2 not implemented for signed integers"),
            .unsigned => return math.log2_int(T, x),
        },
        else => @compileError("log2 not implemented for " ++ @typeName(T)),
    }
}

Functionlog10[src]

pub fn log10(x: anytype) @TypeOf(x)

Returns the base-10 logarithm of x.

Special Cases:

  • log10(+inf) = +inf
  • log10(0) = -inf
  • log10(x) = nan if x < 0
  • log10(nan) = nan

Source Code

Source code
pub fn log10(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    switch (@typeInfo(T)) {
        .comptime_float => {
            return @as(comptime_float, @log10(x));
        },
        .float => return @log10(x),
        .comptime_int => {
            return @as(comptime_int, @floor(@log10(@as(f64, x))));
        },
        .int => |IntType| switch (IntType.signedness) {
            .signed => @compileError("log10 not implemented for signed integers"),
            .unsigned => return log10_int(x),
        },
        else => @compileError("log10 not implemented for " ++ @typeName(T)),
    }
}

Functionlog10_int[src]

pub fn log10_int(x: anytype) std.math.Log2Int(@TypeOf(x))

Return the log base 10 of integer value x, rounding down to the nearest integer.

Example Usage

test log10_int {
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_llvm and comptime builtin.target.cpu.arch.isWasm()) return error.SkipZigTest; // TODO

    inline for (
        .{ u8, u16, u32, u64, u128, u256, u512 },
        .{ 2, 4, 9, 19, 38, 77, 154 },
    ) |T, max_exponent| {
        for (0..max_exponent + 1) |exponent_usize| {
            const exponent: std.math.Log2Int(T) = @intCast(exponent_usize);
            const power_of_ten = try std.math.powi(T, 10, exponent);

            if (exponent > 0) {
                try testing.expectEqual(exponent - 1, log10_int(power_of_ten - 9));
                try testing.expectEqual(exponent - 1, log10_int(power_of_ten - 1));
            }
            try testing.expectEqual(exponent, log10_int(power_of_ten));
            try testing.expectEqual(exponent, log10_int(power_of_ten + 1));
            try testing.expectEqual(exponent, log10_int(power_of_ten + 8));
        }
        try testing.expectEqual(max_exponent, log10_int(@as(T, std.math.maxInt(T))));
    }
}

Source Code

Source code
pub fn log10_int(x: anytype) std.math.Log2Int(@TypeOf(x)) {
    const T = @TypeOf(x);
    const OutT = std.math.Log2Int(T);
    if (@typeInfo(T) != .int or @typeInfo(T).int.signedness != .unsigned)
        @compileError("log10_int requires an unsigned integer, found " ++ @typeName(T));

    std.debug.assert(x != 0);

    const bit_size = @typeInfo(T).int.bits;

    if (bit_size <= 8) {
        return @as(OutT, @intCast(log10_int_u8(x)));
    } else if (bit_size <= 16) {
        return @as(OutT, @intCast(less_than_5(x)));
    }

    var val = x;
    var log: u32 = 0;

    inline for (0..11) |i| {
        // Unnecessary branches should be removed by the compiler
        if (bit_size > (1 << (11 - i)) * 5 * @log2(10.0) and val >= pow10((1 << (11 - i)) * 5)) {
            const num_digits = (1 << (11 - i)) * 5;
            val /= pow10(num_digits);
            log += num_digits;
        }
    }

    if (val >= pow10(5)) {
        val /= pow10(5);
        log += 5;
    }

    return @as(OutT, @intCast(log + less_than_5(@as(u32, @intCast(val)))));
}

Functionlog_int[src]

pub fn log_int(comptime T: type, base: T, x: T) Log2Int(T)

Returns the logarithm of x for the provided base, rounding down to the nearest integer. Asserts that base > 1 and x > 0.

Parameters

T: type
base: T
x: T

Source Code

Source code
pub fn log_int(comptime T: type, base: T, x: T) Log2Int(T) {
    const valid = switch (@typeInfo(T)) {
        .comptime_int => true,
        .int => |IntType| IntType.signedness == .unsigned,
        else => false,
    };
    if (!valid) @compileError("log_int requires an unsigned integer, found " ++ @typeName(T));

    assert(base > 1 and x > 0);
    if (base == 2) return math.log2_int(T, x);

    // Let's denote by [y] the integer part of y.

    // Throughout the iteration the following invariant is preserved:
    //     power = base ^ exponent

    // Safety and termination.
    //
    // We never overflow inside the loop because when we enter the loop we have
    //     power <= [maxInt(T) / base]
    // therefore
    //     power * base <= maxInt(T)
    // is a valid multiplication for type `T` and
    //     exponent + 1 <= log(base, maxInt(T)) <= log2(maxInt(T)) <= maxInt(Log2Int(T))
    // is a valid addition for type `Log2Int(T)`.
    //
    // This implies also termination because power is strictly increasing,
    // hence it must eventually surpass [x / base] < maxInt(T) and we then exit the loop.

    var exponent: Log2Int(T) = 0;
    var power: T = 1;
    while (power <= x / base) {
        power *= base;
        exponent += 1;
    }

    // If we never entered the loop we must have
    //     [x / base] < 1
    // hence
    //     x <= [x / base] * base < base
    // thus the result is 0. We can then return exponent, which is still 0.
    //
    // Otherwise, if we entered the loop at least once,
    // when we exit the loop we have that power is exactly divisible by base and
    //     power / base <= [x / base] < power
    // hence
    //     power <= [x / base] * base <= x < power * base
    // This means that
    //     base^exponent <= x < base^(exponent+1)
    // hence the result is exponent.

    return exponent;
}

Functionlog1p[src]

pub fn log1p(x: anytype) @TypeOf(x)

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

Example Usage

test log1p {
    try expect(log1p(@as(f32, 0.0)) == log1p_32(0.0));
    try expect(log1p(@as(f64, 0.0)) == log1p_64(0.0));
}

Source Code

Source code
pub fn log1p(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    return switch (T) {
        f32 => log1p_32(x),
        f64 => log1p_64(x),
        else => @compileError("log1p not implemented for " ++ @typeName(T)),
    };
}

Functionasinh[src]

pub fn asinh(x: anytype) @TypeOf(x)

Returns the hyperbolic arc-sin of x.

Special Cases:

  • asinh(+-0) = +-0
  • asinh(+-inf) = +-inf
  • asinh(nan) = nan

Example Usage

test asinh {
    try expect(asinh(@as(f32, 0.0)) == asinh32(0.0));
    try expect(asinh(@as(f64, 0.0)) == asinh64(0.0));
}

Source Code

Source code
pub fn asinh(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    return switch (T) {
        f32 => asinh32(x),
        f64 => asinh64(x),
        else => @compileError("asinh not implemented for " ++ @typeName(T)),
    };
}

Functionacosh[src]

pub fn acosh(x: anytype) @TypeOf(x)

Returns the hyperbolic arc-cosine of x.

Special cases:

  • acosh(x) = nan if x < 1
  • acosh(nan) = nan

Example Usage

test acosh {
    try expect(acosh(@as(f32, 1.5)) == acosh32(1.5));
    try expect(acosh(@as(f64, 1.5)) == acosh64(1.5));
}

Source Code

Source code
pub fn acosh(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    return switch (T) {
        f32 => acosh32(x),
        f64 => acosh64(x),
        else => @compileError("acosh not implemented for " ++ @typeName(T)),
    };
}

Functionatanh[src]

pub fn atanh(x: anytype) @TypeOf(x)

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

Example Usage

test atanh {
    try expect(atanh(@as(f32, 0.0)) == atanh_32(0.0));
    try expect(atanh(@as(f64, 0.0)) == atanh_64(0.0));
}

Source Code

Source code
pub fn atanh(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    return switch (T) {
        f32 => atanh_32(x),
        f64 => atanh_64(x),
        else => @compileError("atanh not implemented for " ++ @typeName(T)),
    };
}

Functionsinh[src]

pub fn sinh(x: anytype) @TypeOf(x)

Returns the hyperbolic sine of x.

Special Cases:

  • sinh(+-0) = +-0
  • sinh(+-inf) = +-inf
  • sinh(nan) = nan

Example Usage

test sinh {
    try expect(sinh(@as(f32, 1.5)) == sinh32(1.5));
    try expect(sinh(@as(f64, 1.5)) == sinh64(1.5));
}

Source Code

Source code
pub fn sinh(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    return switch (T) {
        f32 => sinh32(x),
        f64 => sinh64(x),
        else => @compileError("sinh not implemented for " ++ @typeName(T)),
    };
}

Functioncosh[src]

pub fn cosh(x: anytype) @TypeOf(x)

Returns the hyperbolic cosine of x.

Special Cases:

  • cosh(+-0) = 1
  • cosh(+-inf) = +inf
  • cosh(nan) = nan

Example Usage

test cosh {
    try expect(cosh(@as(f32, 1.5)) == cosh32(1.5));
    try expect(cosh(@as(f64, 1.5)) == cosh64(1.5));
}

Source Code

Source code
pub fn cosh(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    return switch (T) {
        f32 => cosh32(x),
        f64 => cosh64(x),
        else => @compileError("cosh not implemented for " ++ @typeName(T)),
    };
}

Functiontanh[src]

pub fn tanh(x: anytype) @TypeOf(x)

Returns the hyperbolic tangent of x.

Special Cases:

  • tanh(+-0) = +-0
  • tanh(+-inf) = +-1
  • tanh(nan) = nan

Example Usage

test tanh {
    try expect(tanh(@as(f32, 1.5)) == tanh32(1.5));
    try expect(tanh(@as(f64, 1.5)) == tanh64(1.5));
}

Source Code

Source code
pub fn tanh(x: anytype) @TypeOf(x) {
    const T = @TypeOf(x);
    return switch (T) {
        f32 => tanh32(x),
        f64 => tanh64(x),
        else => @compileError("tanh not implemented for " ++ @typeName(T)),
    };
}

Functiongcd[src]

pub fn gcd(a: anytype, b: anytype) @TypeOf(a, b)

Returns the greatest common divisor (GCD) of two unsigned integers (a and b) which are not both zero. For example, the GCD of 8 and 12 is 4, that is, gcd(8, 12) == 4.

Example Usage

test gcd {
    const expectEqual = std.testing.expectEqual;

    try expectEqual(gcd(0, 5), 5);
    try expectEqual(gcd(5, 0), 5);
    try expectEqual(gcd(8, 12), 4);
    try expectEqual(gcd(12, 8), 4);
    try expectEqual(gcd(33, 77), 11);
    try expectEqual(gcd(77, 33), 11);
    try expectEqual(gcd(49865, 69811), 9973);
    try expectEqual(gcd(300_000, 2_300_000), 100_000);
    try expectEqual(gcd(90000000_000_000_000_000_000, 2), 2);
    try expectEqual(gcd(@as(u80, 90000000_000_000_000_000_000), 2), 2);
    try expectEqual(gcd(300_000, @as(u32, 2_300_000)), 100_000);
}

Source Code

Source code
pub fn gcd(a: anytype, b: anytype) @TypeOf(a, b) {
    const N = switch (@TypeOf(a, b)) {
        // convert comptime_int to some sized int type for @ctz
        comptime_int => std.math.IntFittingRange(@min(a, b), @max(a, b)),
        else => |T| T,
    };
    if (@typeInfo(N) != .int or @typeInfo(N).int.signedness != .unsigned) {
        @compileError("`a` and `b` must be usigned integers");
    }

    // using an optimised form of Stein's algorithm:
    // https://en.wikipedia.org/wiki/Binary_GCD_algorithm
    std.debug.assert(a != 0 or b != 0);

    if (a == 0) return b;
    if (b == 0) return a;

    var x: N = a;
    var y: N = b;

    const xz = @ctz(x);
    const yz = @ctz(y);
    const shift = @min(xz, yz);
    x >>= @intCast(xz);
    y >>= @intCast(yz);

    var diff = y -% x;
    while (diff != 0) : (diff = y -% x) {
        // ctz is invariant under negation, we
        // put it here to ease data dependencies,
        // makes the CPU happy.
        const zeros = @ctz(diff);
        if (x > y) diff = -%diff;
        y = @min(x, y);
        x = diff >> @intCast(zeros);
    }
    return y << @intCast(shift);
}

Functiongamma[src]

pub fn gamma(comptime T: type, x: T) T

Returns the gamma function of x, gamma(x) = factorial(x - 1) for integer x.

Special Cases:

  • gamma(+-nan) = nan
  • gamma(-inf) = nan
  • gamma(n) = nan for negative integers
  • gamma(-0.0) = -inf
  • gamma(+0.0) = +inf
  • gamma(+inf) = +inf

Parameters

T: type
x: T

Example Usage

test gamma {
    inline for (&.{ f32, f64 }) |T| {
        const eps = @sqrt(std.math.floatEps(T));
        try expectApproxEqRel(@as(T, 120), gamma(T, 6), eps);
        try expectApproxEqRel(@as(T, 362880), gamma(T, 10), eps);
        try expectApproxEqRel(@as(T, 6402373705728000), gamma(T, 19), eps);

        try expectApproxEqRel(@as(T, 332.7590766955334570), gamma(T, 0.003), eps);
        try expectApproxEqRel(@as(T, 1.377260301981044573), gamma(T, 0.654), eps);
        try expectApproxEqRel(@as(T, 1.025393882573518478), gamma(T, 0.959), eps);

        try expectApproxEqRel(@as(T, 7.361898021467681690), gamma(T, 4.16), eps);
        try expectApproxEqRel(@as(T, 198337.2940287730753), gamma(T, 9.73), eps);
        try expectApproxEqRel(@as(T, 113718145797241.1666), gamma(T, 17.6), eps);

        try expectApproxEqRel(@as(T, -1.13860211111081424930673), gamma(T, -2.80), eps);
        try expectApproxEqRel(@as(T, 0.00018573407931875070158), gamma(T, -7.74), eps);
        try expectApproxEqRel(@as(T, -0.00000001647990903942825), gamma(T, -12.1), eps);
    }
}

Source Code

Source code
pub fn gamma(comptime T: type, x: T) T {
    if (T != f32 and T != f64) {
        @compileError("gamma not implemented for " ++ @typeName(T));
    }
    // common integer case first
    if (x == @trunc(x)) {
        // gamma(-inf) = nan
        // gamma(n)    = nan for negative integers
        if (x < 0) {
            return std.math.nan(T);
        }
        // gamma(-0.0) = -inf
        // gamma(+0.0) = +inf
        if (x == 0) {
            return 1 / x;
        }
        if (x < integer_result_table.len) {
            const i = @as(u8, @intFromFloat(x));
            return @floatCast(integer_result_table[i]);
        }
    }
    // below this, result underflows, but has a sign
    // negative for (-1,  0)
    // positive for (-2, -1)
    // negative for (-3, -2)
    // ...
    const lower_bound = if (T == f64) -184 else -42;
    if (x < lower_bound) {
        return if (@mod(x, 2) > 1) -0.0 else 0.0;
    }
    // above this, result overflows
    // gamma(+inf) = +inf
    const upper_bound = if (T == f64) 172 else 36;
    if (x > upper_bound) {
        return std.math.inf(T);
    }

    const abs = @abs(x);
    // perfect precision here
    if (abs < 0x1p-54) {
        return 1 / x;
    }

    const base = abs + lanczos_minus_half;
    const exponent = abs - 0.5;
    // error of y for correction, see
    // https://github.com/python/cpython/blob/5dc79e3d7f26a6a871a89ce3efc9f1bcee7bb447/Modules/mathmodule.c#L286-L324
    const e = if (abs > lanczos_minus_half)
        base - abs - lanczos_minus_half
    else
        base - lanczos_minus_half - abs;
    const correction = lanczos * e / base;
    const initial = series(T, abs) * @exp(-base);

    // use reflection formula for negatives
    if (x < 0) {
        const reflected = -std.math.pi / (abs * sinpi(T, abs) * initial);
        const corrected = reflected - reflected * correction;
        const half_pow = std.math.pow(T, base, -0.5 * exponent);
        return corrected * half_pow * half_pow;
    } else {
        const corrected = initial + initial * correction;
        const half_pow = std.math.pow(T, base, 0.5 * exponent);
        return corrected * half_pow * half_pow;
    }
}

Functionlgamma[src]

pub fn lgamma(comptime T: type, x: T) T

Returns the natural logarithm of the absolute value of the gamma function.

Special Cases:

  • lgamma(+-nan) = nan
  • lgamma(+-inf) = +inf
  • lgamma(n) = +inf for negative integers
  • lgamma(+-0.0) = +inf
  • lgamma(1) = +0.0
  • lgamma(2) = +0.0

Parameters

T: type
x: T

Example Usage

test lgamma {
    inline for (&.{ f32, f64 }) |T| {
        const eps = @sqrt(std.math.floatEps(T));
        try expectApproxEqRel(@as(T, @log(24.0)), lgamma(T, 5), eps);
        try expectApproxEqRel(@as(T, @log(20922789888000.0)), lgamma(T, 17), eps);
        try expectApproxEqRel(@as(T, @log(2432902008176640000.0)), lgamma(T, 21), eps);

        try expectApproxEqRel(@as(T, 2.201821590438859327), lgamma(T, 0.105), eps);
        try expectApproxEqRel(@as(T, 1.275416975248413231), lgamma(T, 0.253), eps);
        try expectApproxEqRel(@as(T, 0.130463884049976732), lgamma(T, 0.823), eps);

        try expectApproxEqRel(@as(T, 43.24395772148497989), lgamma(T, 21.3), eps);
        try expectApproxEqRel(@as(T, 110.6908958012102623), lgamma(T, 41.1), eps);
        try expectApproxEqRel(@as(T, 215.2123266224689711), lgamma(T, 67.4), eps);

        try expectApproxEqRel(@as(T, -122.605958469563489), lgamma(T, -43.6), eps);
        try expectApproxEqRel(@as(T, -278.633885462703133), lgamma(T, -81.4), eps);
        try expectApproxEqRel(@as(T, -333.247676253238363), lgamma(T, -93.6), eps);
    }
}

Source Code

Source code
pub fn lgamma(comptime T: type, x: T) T {
    if (T != f32 and T != f64) {
        @compileError("gamma not implemented for " ++ @typeName(T));
    }
    // common integer case first
    if (x == @trunc(x)) {
        // lgamma(-inf)  = +inf
        // lgamma(n)     = +inf for negative integers
        // lgamma(+-0.0) = +inf
        if (x <= 0) {
            return std.math.inf(T);
        }
        // lgamma(1) = +0.0
        // lgamma(2) = +0.0
        if (x < integer_result_table.len) {
            const i = @as(u8, @intFromFloat(x));
            return @log(@as(T, @floatCast(integer_result_table[i])));
        }
        // lgamma(+inf) = +inf
        if (std.math.isPositiveInf(x)) {
            return x;
        }
    }

    const abs = @abs(x);
    // perfect precision here
    if (abs < 0x1p-54) {
        return -@log(abs);
    }
    // obvious approach when overflow is not a problem
    const upper_bound = if (T == f64) 128 else 26;
    if (abs < upper_bound) {
        return @log(@abs(gamma(T, x)));
    }

    const log_base = @log(abs + lanczos_minus_half) - 1;
    const exponent = abs - 0.5;
    const log_series = @log(series(T, abs));
    const initial = exponent * log_base + log_series - lanczos;

    // use reflection formula for negatives
    if (x < 0) {
        const reflected = std.math.pi / (abs * sinpi(T, abs));
        return @log(@abs(reflected)) - initial;
    }
    return initial;
}

Functionsin[src]

pub inline fn sin(value: anytype) @TypeOf(value)

Sine trigonometric function on a floating point number. Uses a dedicated hardware instruction when available. This is the same as calling the builtin @sin

Source Code

Source code
pub inline fn sin(value: anytype) @TypeOf(value) {
    return @sin(value);
}

Functioncos[src]

pub inline fn cos(value: anytype) @TypeOf(value)

Cosine trigonometric function on a floating point number. Uses a dedicated hardware instruction when available. This is the same as calling the builtin @cos

Source Code

Source code
pub inline fn cos(value: anytype) @TypeOf(value) {
    return @cos(value);
}

Functiontan[src]

pub inline fn tan(value: anytype) @TypeOf(value)

Tangent trigonometric function on a floating point number. Uses a dedicated hardware instruction when available. This is the same as calling the builtin @tan

Source Code

Source code
pub inline fn tan(value: anytype) @TypeOf(value) {
    return @tan(value);
}

FunctionradiansToDegrees[src]

pub fn radiansToDegrees(ang: anytype) if (@TypeOf(ang) == comptime_int) comptime_float else @TypeOf(ang)

Converts an angle in radians to degrees. T must be a float or comptime number or a vector of floats.

Example Usage

test radiansToDegrees {
    const zero: f32 = 0;
    const half_pi: f32 = pi / 2.0;
    const neg_quart_pi: f32 = -pi / 4.0;
    const one_pi: f32 = pi;
    const two_pi: f32 = 2.0 * pi;
    try std.testing.expectApproxEqAbs(@as(f32, 0), radiansToDegrees(zero), 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 90), radiansToDegrees(half_pi), 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, -45), radiansToDegrees(neg_quart_pi), 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 180), radiansToDegrees(one_pi), 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 360), radiansToDegrees(two_pi), 1e-6);

    const result = radiansToDegrees(@Vector(4, f32){
        half_pi,
        neg_quart_pi,
        one_pi,
        two_pi,
    });
    try std.testing.expectApproxEqAbs(@as(f32, 90), result[0], 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, -45), result[1], 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 180), result[2], 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 360), result[3], 1e-6);
}

Source Code

Source code
pub fn radiansToDegrees(ang: anytype) if (@TypeOf(ang) == comptime_int) comptime_float else @TypeOf(ang) {
    const T = @TypeOf(ang);
    switch (@typeInfo(T)) {
        .float, .comptime_float, .comptime_int => return ang * deg_per_rad,
        .vector => |V| if (@typeInfo(V.child) == .float) return ang * @as(T, @splat(deg_per_rad)),
        else => {},
    }
    @compileError("Input must be float or a comptime number, or a vector of floats.");
}

FunctiondegreesToRadians[src]

pub fn degreesToRadians(ang: anytype) if (@TypeOf(ang) == comptime_int) comptime_float else @TypeOf(ang)

Converts an angle in degrees to radians. T must be a float or comptime number or a vector of floats.

Example Usage

test degreesToRadians {
    const ninety: f32 = 90;
    const neg_two_seventy: f32 = -270;
    const three_sixty: f32 = 360;
    try std.testing.expectApproxEqAbs(@as(f32, pi / 2.0), degreesToRadians(ninety), 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, -3 * pi / 2.0), degreesToRadians(neg_two_seventy), 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 2 * pi), degreesToRadians(three_sixty), 1e-6);

    const result = degreesToRadians(@Vector(3, f32){
        ninety,
        neg_two_seventy,
        three_sixty,
    });
    try std.testing.expectApproxEqAbs(@as(f32, pi / 2.0), result[0], 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, -3 * pi / 2.0), result[1], 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 2 * pi), result[2], 1e-6);
}

Source Code

Source code
pub fn degreesToRadians(ang: anytype) if (@TypeOf(ang) == comptime_int) comptime_float else @TypeOf(ang) {
    const T = @TypeOf(ang);
    switch (@typeInfo(T)) {
        .float, .comptime_float, .comptime_int => return ang * rad_per_deg,
        .vector => |V| if (@typeInfo(V.child) == .float) return ang * @as(T, @splat(rad_per_deg)),
        else => {},
    }
    @compileError("Input must be float or a comptime number, or a vector of floats.");
}

Functionexp[src]

pub inline fn exp(value: anytype) @TypeOf(value)

Base-e exponential function on a floating point number. Uses a dedicated hardware instruction when available. This is the same as calling the builtin @exp

Source Code

Source code
pub inline fn exp(value: anytype) @TypeOf(value) {
    return @exp(value);
}

Functionexp2[src]

pub inline fn exp2(value: anytype) @TypeOf(value)

Base-2 exponential function on a floating point number. Uses a dedicated hardware instruction when available. This is the same as calling the builtin @exp2

Source Code

Source code
pub inline fn exp2(value: anytype) @TypeOf(value) {
    return @exp2(value);
}

Functionwrap[src]

pub fn wrap(x: anytype, r: anytype) @TypeOf(x)

Odd sawtooth function

        |
     /  | /    /
    /   |/    /
 --/----/----/--
  /    /|   /
 /    / |  /
        |

Limit x to the half-open interval [-r, r).

Example Usage

test wrap {
    // Within range
    try testing.expect(wrap(@as(i32, -75), @as(i32, 180)) == -75);
    try testing.expect(wrap(@as(i32, -75), @as(i32, -180)) == -75);
    // Below
    try testing.expect(wrap(@as(i32, -225), @as(i32, 180)) == 135);
    try testing.expect(wrap(@as(i32, -225), @as(i32, -180)) == 135);
    // Above
    try testing.expect(wrap(@as(i32, 361), @as(i32, 180)) == 1);
    try testing.expect(wrap(@as(i32, 361), @as(i32, -180)) == 1);

    // One period, right limit, positive r
    try testing.expect(wrap(@as(i32, 180), @as(i32, 180)) == -180);
    // One period, left limit, positive r
    try testing.expect(wrap(@as(i32, -180), @as(i32, 180)) == -180);
    // One period, right limit, negative r
    try testing.expect(wrap(@as(i32, 180), @as(i32, -180)) == 180);
    // One period, left limit, negative r
    try testing.expect(wrap(@as(i32, -180), @as(i32, -180)) == 180);

    // Two periods, right limit, positive r
    try testing.expect(wrap(@as(i32, 540), @as(i32, 180)) == -180);
    // Two periods, left limit, positive r
    try testing.expect(wrap(@as(i32, -540), @as(i32, 180)) == -180);
    // Two periods, right limit, negative r
    try testing.expect(wrap(@as(i32, 540), @as(i32, -180)) == 180);
    // Two periods, left limit, negative r
    try testing.expect(wrap(@as(i32, -540), @as(i32, -180)) == 180);

    // Floating point
    try testing.expect(wrap(@as(f32, 1.125), @as(f32, 1.0)) == -0.875);
    try testing.expect(wrap(@as(f32, -127.5), @as(f32, 180)) == -127.5);

    // Mix of comptime and non-comptime
    var i: i32 = 1;
    _ = &i;
    try testing.expect(wrap(i, 10) == 1);

    const limit: i32 = 180;
    // Within range
    try testing.expect(wrap(@as(i32, -75), limit) == -75);
    // Below
    try testing.expect(wrap(@as(i32, -225), limit) == 135);
    // Above
    try testing.expect(wrap(@as(i32, 361), limit) == 1);
}

Source Code

Source code
pub fn wrap(x: anytype, r: anytype) @TypeOf(x) {
    const info_x = @typeInfo(@TypeOf(x));
    const info_r = @typeInfo(@TypeOf(r));
    if (info_x == .int and info_x.int.signedness != .signed) {
        @compileError("x must be floating point, comptime integer, or signed integer.");
    }
    switch (info_r) {
        .int => {
            // in the rare usecase of r not being comptime_int or float,
            // take the penalty of having an intermediary type conversion,
            // otherwise the alternative is to unwind iteratively to avoid overflow
            const R = comptime do: {
                var info = info_r;
                info.int.bits += 1;
                info.int.signedness = .signed;
                break :do @Type(info);
            };
            const radius: if (info_r.int.signedness == .signed) @TypeOf(r) else R = r;
            return @intCast(@mod(x - radius, 2 * @as(R, r)) - r); // provably impossible to overflow
        },
        else => {
            return @mod(x - r, 2 * r) - r;
        },
    }
}

Functionclamp[src]

pub fn clamp(val: anytype, lower: anytype, upper: anytype) @TypeOf(val, lower, upper)

Odd ramp function

        |  _____
        | /
        |/
 -------/-------
       /|
 _____/ |
        |

Limit val to the inclusive range [lower, upper].

Example Usage

test clamp {
    // Within range
    try testing.expect(std.math.clamp(@as(i32, -1), @as(i32, -4), @as(i32, 7)) == -1);
    // Below
    try testing.expect(std.math.clamp(@as(i32, -5), @as(i32, -4), @as(i32, 7)) == -4);
    // Above
    try testing.expect(std.math.clamp(@as(i32, 8), @as(i32, -4), @as(i32, 7)) == 7);

    // Floating point
    try testing.expect(std.math.clamp(@as(f32, 1.1), @as(f32, 0.0), @as(f32, 1.0)) == 1.0);
    try testing.expect(std.math.clamp(@as(f32, -127.5), @as(f32, -200), @as(f32, -100)) == -127.5);

    // Vector
    try testing.expect(@reduce(.And, std.math.clamp(@as(@Vector(3, f32), .{ 1.4, 15.23, 28.3 }), @as(@Vector(3, f32), .{ 9.8, 13.2, 15.6 }), @as(@Vector(3, f32), .{ 15.2, 22.8, 26.3 })) == @as(@Vector(3, f32), .{ 9.8, 15.23, 26.3 })));

    // Mix of comptime and non-comptime
    var i: i32 = 1;
    _ = &i;
    try testing.expect(std.math.clamp(i, 0, 1) == 1);
}

Source Code

Source code
pub fn clamp(val: anytype, lower: anytype, upper: anytype) @TypeOf(val, lower, upper) {
    const T = @TypeOf(val, lower, upper);
    switch (@typeInfo(T)) {
        .int, .float, .comptime_int, .comptime_float => assert(lower <= upper),
        .vector => |vinfo| switch (@typeInfo(vinfo.child)) {
            .int, .float => assert(@reduce(.And, lower <= upper)),
            else => @compileError("Expected vector of ints or floats, found " ++ @typeName(T)),
        },
        else => @compileError("Expected an int, float or vector of one, found " ++ @typeName(T)),
    }
    return @max(lower, @min(val, upper));
}

Functionmul[src]

pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T)

Returns the product of a and b. Returns an error on overflow.

Parameters

T: type
a: T
b: T

Source Code

Source code
pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) {
    if (T == comptime_int) return a * b;
    const ov = @mulWithOverflow(a, b);
    if (ov[1] != 0) return error.Overflow;
    return ov[0];
}

Functionadd[src]

pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T)

Returns the sum of a and b. Returns an error on overflow.

Parameters

T: type
a: T
b: T

Source Code

Source code
pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) {
    if (T == comptime_int) return a + b;
    const ov = @addWithOverflow(a, b);
    if (ov[1] != 0) return error.Overflow;
    return ov[0];
}

Functionsub[src]

pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T)

Returns a - b, or an error on overflow.

Parameters

T: type
a: T
b: T

Source Code

Source code
pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) {
    if (T == comptime_int) return a - b;
    const ov = @subWithOverflow(a, b);
    if (ov[1] != 0) return error.Overflow;
    return ov[0];
}

Functionnegate[src]

pub fn negate(x: anytype) !@TypeOf(x)

Source Code

Source code
pub fn negate(x: anytype) !@TypeOf(x) {
    return sub(@TypeOf(x), 0, x);
}

FunctionshlExact[src]

pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T

Shifts a left by shift_amt. Returns an error on overflow. shift_amt is unsigned.

Parameters

T: type
a: T
shift_amt: Log2Int(T)

Source Code

Source code
pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {
    if (T == comptime_int) return a << shift_amt;
    const ov = @shlWithOverflow(a, shift_amt);
    if (ov[1] != 0) return error.Overflow;
    return ov[0];
}

Functionshl[src]

pub fn shl(comptime T: type, a: T, shift_amt: anytype) T

Shifts left. Overflowed bits are truncated. A negative shift amount results in a right shift.

Parameters

T: type
a: T

Example Usage

test shl {
    try testing.expect(shl(u8, 0b11111111, @as(usize, 3)) == 0b11111000);
    try testing.expect(shl(u8, 0b11111111, @as(usize, 8)) == 0);
    try testing.expect(shl(u8, 0b11111111, @as(usize, 9)) == 0);
    try testing.expect(shl(u8, 0b11111111, @as(isize, -2)) == 0b00111111);
    try testing.expect(shl(u8, 0b11111111, 3) == 0b11111000);
    try testing.expect(shl(u8, 0b11111111, 8) == 0);
    try testing.expect(shl(u8, 0b11111111, 9) == 0);
    try testing.expect(shl(u8, 0b11111111, -2) == 0b00111111);
    try testing.expect(shl(@Vector(1, u32), @Vector(1, u32){42}, @as(usize, 1))[0] == @as(u32, 42) << 1);
    try testing.expect(shl(@Vector(1, u32), @Vector(1, u32){42}, @as(isize, -1))[0] == @as(u32, 42) >> 1);
    try testing.expect(shl(@Vector(1, u32), @Vector(1, u32){42}, 33)[0] == 0);
}

Source Code

Source code
pub fn shl(comptime T: type, a: T, shift_amt: anytype) T {
    const abs_shift_amt = @abs(shift_amt);

    const casted_shift_amt = blk: {
        if (@typeInfo(T) == .vector) {
            const C = @typeInfo(T).vector.child;
            const len = @typeInfo(T).vector.len;
            if (abs_shift_amt >= @typeInfo(C).int.bits) return @splat(0);
            break :blk @as(@Vector(len, Log2Int(C)), @splat(@as(Log2Int(C), @intCast(abs_shift_amt))));
        } else {
            if (abs_shift_amt >= @typeInfo(T).int.bits) return 0;
            break :blk @as(Log2Int(T), @intCast(abs_shift_amt));
        }
    };

    if (@TypeOf(shift_amt) == comptime_int or @typeInfo(@TypeOf(shift_amt)).int.signedness == .signed) {
        if (shift_amt < 0) {
            return a >> casted_shift_amt;
        }
    }

    return a << casted_shift_amt;
}

Functionshr[src]

pub fn shr(comptime T: type, a: T, shift_amt: anytype) T

Shifts right. Overflowed bits are truncated. A negative shift amount results in a left shift.

Parameters

T: type
a: T

Example Usage

test shr {
    try testing.expect(shr(u8, 0b11111111, @as(usize, 3)) == 0b00011111);
    try testing.expect(shr(u8, 0b11111111, @as(usize, 8)) == 0);
    try testing.expect(shr(u8, 0b11111111, @as(usize, 9)) == 0);
    try testing.expect(shr(u8, 0b11111111, @as(isize, -2)) == 0b11111100);
    try testing.expect(shr(u8, 0b11111111, 3) == 0b00011111);
    try testing.expect(shr(u8, 0b11111111, 8) == 0);
    try testing.expect(shr(u8, 0b11111111, 9) == 0);
    try testing.expect(shr(u8, 0b11111111, -2) == 0b11111100);
    try testing.expect(shr(@Vector(1, u32), @Vector(1, u32){42}, @as(usize, 1))[0] == @as(u32, 42) >> 1);
    try testing.expect(shr(@Vector(1, u32), @Vector(1, u32){42}, @as(isize, -1))[0] == @as(u32, 42) << 1);
    try testing.expect(shr(@Vector(1, u32), @Vector(1, u32){42}, 33)[0] == 0);
}

Source Code

Source code
pub fn shr(comptime T: type, a: T, shift_amt: anytype) T {
    const abs_shift_amt = @abs(shift_amt);

    const casted_shift_amt = blk: {
        if (@typeInfo(T) == .vector) {
            const C = @typeInfo(T).vector.child;
            const len = @typeInfo(T).vector.len;
            if (abs_shift_amt >= @typeInfo(C).int.bits) return @splat(0);
            break :blk @as(@Vector(len, Log2Int(C)), @splat(@as(Log2Int(C), @intCast(abs_shift_amt))));
        } else {
            if (abs_shift_amt >= @typeInfo(T).int.bits) return 0;
            break :blk @as(Log2Int(T), @intCast(abs_shift_amt));
        }
    };

    if (@TypeOf(shift_amt) == comptime_int or @typeInfo(@TypeOf(shift_amt)).int.signedness == .signed) {
        if (shift_amt < 0) {
            return a << casted_shift_amt;
        }
    }

    return a >> casted_shift_amt;
}

Functionrotr[src]

pub fn rotr(comptime T: type, x: T, r: anytype) T

Rotates right. Only unsigned values can be rotated. Negative shift values result in shift modulo the bit count.

Parameters

T: type
x: T

Example Usage

test rotr {
    try testing.expect(rotr(u0, 0b0, @as(usize, 3)) == 0b0);
    try testing.expect(rotr(u5, 0b00001, @as(usize, 0)) == 0b00001);
    try testing.expect(rotr(u6, 0b000001, @as(usize, 7)) == 0b100000);
    try testing.expect(rotr(u8, 0b00000001, @as(usize, 0)) == 0b00000001);
    try testing.expect(rotr(u8, 0b00000001, @as(usize, 9)) == 0b10000000);
    try testing.expect(rotr(u8, 0b00000001, @as(usize, 8)) == 0b00000001);
    try testing.expect(rotr(u8, 0b00000001, @as(usize, 4)) == 0b00010000);
    try testing.expect(rotr(u8, 0b00000001, @as(isize, -1)) == 0b00000010);
    try testing.expect(rotr(u12, 0o7777, 1) == 0o7777);
    try testing.expect(rotr(@Vector(1, u32), @Vector(1, u32){1}, @as(usize, 1))[0] == @as(u32, 1) << 31);
    try testing.expect(rotr(@Vector(1, u32), @Vector(1, u32){1}, @as(isize, -1))[0] == @as(u32, 1) << 1);
}

Source Code

Source code
pub fn rotr(comptime T: type, x: T, r: anytype) T {
    if (@typeInfo(T) == .vector) {
        const C = @typeInfo(T).vector.child;
        if (C == u0) return 0;

        if (@typeInfo(C).int.signedness == .signed) {
            @compileError("cannot rotate signed integers");
        }
        const ar: Log2Int(C) = @intCast(@mod(r, @typeInfo(C).int.bits));
        return (x >> @splat(ar)) | (x << @splat(1 + ~ar));
    } else if (@typeInfo(T).int.signedness == .signed) {
        @compileError("cannot rotate signed integer");
    } else {
        if (T == u0) return 0;

        if (comptime isPowerOfTwo(@typeInfo(T).int.bits)) {
            const ar: Log2Int(T) = @intCast(@mod(r, @typeInfo(T).int.bits));
            return x >> ar | x << (1 +% ~ar);
        } else {
            const ar = @mod(r, @typeInfo(T).int.bits);
            return shr(T, x, ar) | shl(T, x, @typeInfo(T).int.bits - ar);
        }
    }
}

Functionrotl[src]

pub fn rotl(comptime T: type, x: T, r: anytype) T

Rotates left. Only unsigned values can be rotated. Negative shift values result in shift modulo the bit count.

Parameters

T: type
x: T

Example Usage

test rotl {
    try testing.expect(rotl(u0, 0b0, @as(usize, 3)) == 0b0);
    try testing.expect(rotl(u5, 0b00001, @as(usize, 0)) == 0b00001);
    try testing.expect(rotl(u6, 0b000001, @as(usize, 7)) == 0b000010);
    try testing.expect(rotl(u8, 0b00000001, @as(usize, 0)) == 0b00000001);
    try testing.expect(rotl(u8, 0b00000001, @as(usize, 9)) == 0b00000010);
    try testing.expect(rotl(u8, 0b00000001, @as(usize, 8)) == 0b00000001);
    try testing.expect(rotl(u8, 0b00000001, @as(usize, 4)) == 0b00010000);
    try testing.expect(rotl(u8, 0b00000001, @as(isize, -1)) == 0b10000000);
    try testing.expect(rotl(u12, 0o7777, 1) == 0o7777);
    try testing.expect(rotl(@Vector(1, u32), @Vector(1, u32){1 << 31}, @as(usize, 1))[0] == 1);
    try testing.expect(rotl(@Vector(1, u32), @Vector(1, u32){1 << 31}, @as(isize, -1))[0] == @as(u32, 1) << 30);
}

Source Code

Source code
pub fn rotl(comptime T: type, x: T, r: anytype) T {
    if (@typeInfo(T) == .vector) {
        const C = @typeInfo(T).vector.child;
        if (C == u0) return 0;

        if (@typeInfo(C).int.signedness == .signed) {
            @compileError("cannot rotate signed integers");
        }
        const ar: Log2Int(C) = @intCast(@mod(r, @typeInfo(C).int.bits));
        return (x << @splat(ar)) | (x >> @splat(1 +% ~ar));
    } else if (@typeInfo(T).int.signedness == .signed) {
        @compileError("cannot rotate signed integer");
    } else {
        if (T == u0) return 0;

        if (comptime isPowerOfTwo(@typeInfo(T).int.bits)) {
            const ar: Log2Int(T) = @intCast(@mod(r, @typeInfo(T).int.bits));
            return x << ar | x >> 1 +% ~ar;
        } else {
            const ar = @mod(r, @typeInfo(T).int.bits);
            return shl(T, x, ar) | shr(T, x, @typeInfo(T).int.bits - ar);
        }
    }
}

FunctiondivTrunc[src]

pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T

Divide numerator by denominator, rounding toward zero. Returns an error on overflow or when denominator is zero.

Parameters

T: type
numerator: T
denominator: T

Example Usage

test divTrunc {
    try testDivTrunc();
    try comptime testDivTrunc();
}

Source Code

Source code
pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T {
    @setRuntimeSafety(false);
    if (denominator == 0) return error.DivisionByZero;
    if (@typeInfo(T) == .int and @typeInfo(T).int.signedness == .signed and numerator == minInt(T) and denominator == -1) return error.Overflow;
    return @divTrunc(numerator, denominator);
}

FunctiondivFloor[src]

pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T

Divide numerator by denominator, rounding toward negative infinity. Returns an error on overflow or when denominator is zero.

Parameters

T: type
numerator: T
denominator: T

Example Usage

test divFloor {
    try testDivFloor();
    try comptime testDivFloor();
}

Source Code

Source code
pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T {
    @setRuntimeSafety(false);
    if (denominator == 0) return error.DivisionByZero;
    if (@typeInfo(T) == .int and @typeInfo(T).int.signedness == .signed and numerator == minInt(T) and denominator == -1) return error.Overflow;
    return @divFloor(numerator, denominator);
}

FunctiondivCeil[src]

pub fn divCeil(comptime T: type, numerator: T, denominator: T) !T

Divide numerator by denominator, rounding toward positive infinity. Returns an error on overflow or when denominator is zero.

Parameters

T: type
numerator: T
denominator: T

Example Usage

test divCeil {
    try testDivCeil();
    try comptime testDivCeil();
}

Source Code

Source code
pub fn divCeil(comptime T: type, numerator: T, denominator: T) !T {
    @setRuntimeSafety(false);
    if (denominator == 0) return error.DivisionByZero;
    const info = @typeInfo(T);
    switch (info) {
        .comptime_float, .float => return @ceil(numerator / denominator),
        .comptime_int, .int => {
            if (numerator < 0 and denominator < 0) {
                if (info == .int and numerator == minInt(T) and denominator == -1)
                    return error.Overflow;
                return @divFloor(numerator + 1, denominator) + 1;
            }
            if (numerator > 0 and denominator > 0)
                return @divFloor(numerator - 1, denominator) + 1;
            return @divTrunc(numerator, denominator);
        },
        else => @compileError("divCeil unsupported on " ++ @typeName(T)),
    }
}

FunctiondivExact[src]

pub fn divExact(comptime T: type, numerator: T, denominator: T) !T

Divide numerator by denominator. Return an error if quotient is not an integer, denominator is zero, or on overflow.

Parameters

T: type
numerator: T
denominator: T

Example Usage

test divExact {
    try testDivExact();
    try comptime testDivExact();
}

Source Code

Source code
pub fn divExact(comptime T: type, numerator: T, denominator: T) !T {
    @setRuntimeSafety(false);
    if (denominator == 0) return error.DivisionByZero;
    if (@typeInfo(T) == .int and @typeInfo(T).int.signedness == .signed and numerator == minInt(T) and denominator == -1) return error.Overflow;
    const result = @divTrunc(numerator, denominator);
    if (result * denominator != numerator) return error.UnexpectedRemainder;
    return result;
}

Functionmod[src]

pub fn mod(comptime T: type, numerator: T, denominator: T) !T

Returns numerator modulo denominator, or an error if denominator is zero or negative. Negative numerators never result in negative return values.

Parameters

T: type
numerator: T
denominator: T

Example Usage

test mod {
    try testMod();
    try comptime testMod();
}

Source Code

Source code
pub fn mod(comptime T: type, numerator: T, denominator: T) !T {
    @setRuntimeSafety(false);
    if (denominator == 0) return error.DivisionByZero;
    if (denominator < 0) return error.NegativeDenominator;
    return @mod(numerator, denominator);
}

Functionrem[src]

pub fn rem(comptime T: type, numerator: T, denominator: T) !T

Returns the remainder when numerator is divided by denominator, or an error if denominator is zero or negative. Negative numerators can give negative results.

Parameters

T: type
numerator: T
denominator: T

Example Usage

test rem {
    try testRem();
    try comptime testRem();
}

Source Code

Source code
pub fn rem(comptime T: type, numerator: T, denominator: T) !T {
    @setRuntimeSafety(false);
    if (denominator == 0) return error.DivisionByZero;
    if (denominator < 0) return error.NegativeDenominator;
    return @rem(numerator, denominator);
}

FunctionnegateCast[src]

pub fn negateCast(x: anytype) !std.meta.Int(.signed, @bitSizeOf(@TypeOf(x)))

Returns the negation of the integer parameter. Result is a signed integer.

Example Usage

test negateCast {
    try testing.expect((negateCast(@as(u32, 999)) catch unreachable) == -999);
    try testing.expect(@TypeOf(negateCast(@as(u32, 999)) catch unreachable) == i32);

    try testing.expect((negateCast(@as(u32, -minInt(i32))) catch unreachable) == minInt(i32));
    try testing.expect(@TypeOf(negateCast(@as(u32, -minInt(i32))) catch unreachable) == i32);

    try testing.expectError(error.Overflow, negateCast(@as(u32, maxInt(i32) + 10)));
}

Source Code

Source code
pub fn negateCast(x: anytype) !std.meta.Int(.signed, @bitSizeOf(@TypeOf(x))) {
    if (@typeInfo(@TypeOf(x)).int.signedness == .signed) return negate(x);

    const int = std.meta.Int(.signed, @bitSizeOf(@TypeOf(x)));
    if (x > -minInt(int)) return error.Overflow;

    if (x == -minInt(int)) return minInt(int);

    return -@as(int, @intCast(x));
}

Functioncast[src]

pub fn cast(comptime T: type, x: anytype) ?T

Cast an integer to a different integer type. If the value doesn't fit, return null.

Parameters

T: type

Example Usage

test cast {
    try testing.expect(cast(u8, 300) == null);
    try testing.expect(cast(u8, @as(u32, 300)) == null);
    try testing.expect(cast(i8, -200) == null);
    try testing.expect(cast(i8, @as(i32, -200)) == null);
    try testing.expect(cast(u8, -1) == null);
    try testing.expect(cast(u8, @as(i8, -1)) == null);
    try testing.expect(cast(u64, -1) == null);
    try testing.expect(cast(u64, @as(i8, -1)) == null);

    try testing.expect(cast(u8, 255).? == @as(u8, 255));
    try testing.expect(cast(u8, @as(u32, 255)).? == @as(u8, 255));
    try testing.expect(@TypeOf(cast(u8, 255).?) == u8);
    try testing.expect(@TypeOf(cast(u8, @as(u32, 255)).?) == u8);
}

Source Code

Source code
pub fn cast(comptime T: type, x: anytype) ?T {
    comptime assert(@typeInfo(T) == .int); // must pass an integer
    const is_comptime = @TypeOf(x) == comptime_int;
    comptime assert(is_comptime or @typeInfo(@TypeOf(x)) == .int); // must pass an integer
    if ((is_comptime or maxInt(@TypeOf(x)) > maxInt(T)) and x > maxInt(T)) {
        return null;
    } else if ((is_comptime or minInt(@TypeOf(x)) < minInt(T)) and x < minInt(T)) {
        return null;
    } else {
        return @as(T, @intCast(x));
    }
}

FunctionalignCast[src]

pub fn alignCast(comptime alignment: u29, ptr: anytype) AlignCastError!AlignCastResult(alignment, @TypeOf(ptr))

Align cast a pointer but return an error if it's the wrong alignment

Parameters

alignment: u29

Source Code

Source code
pub fn alignCast(comptime alignment: u29, ptr: anytype) AlignCastError!AlignCastResult(alignment, @TypeOf(ptr)) {
    const addr = @intFromPtr(ptr);
    if (addr % alignment != 0) {
        return error.UnalignedMemory;
    }
    return @alignCast(ptr);
}

FunctionisPowerOfTwo[src]

pub fn isPowerOfTwo(int: anytype) bool

Asserts int > 0.

Example Usage

test isPowerOfTwo {
    try testing.expect(isPowerOfTwo(@as(u8, 1)));
    try testing.expect(isPowerOfTwo(2));
    try testing.expect(!isPowerOfTwo(@as(i16, 3)));
    try testing.expect(isPowerOfTwo(4));
    try testing.expect(!isPowerOfTwo(@as(u32, 31)));
    try testing.expect(isPowerOfTwo(32));
    try testing.expect(!isPowerOfTwo(@as(i64, 63)));
    try testing.expect(isPowerOfTwo(128));
    try testing.expect(isPowerOfTwo(@as(u128, 256)));
}

Source Code

Source code
pub fn isPowerOfTwo(int: anytype) bool {
    assert(int > 0);
    return (int & (int - 1)) == 0;
}

Functionround[src]

pub inline fn round(value: anytype) @TypeOf(value)

Rounds the given floating point number to the nearest integer. If two integers are equally close, rounds away from zero. Uses a dedicated hardware instruction when available. This is the same as calling the builtin @round

Source Code

Source code
pub inline fn round(value: anytype) @TypeOf(value) {
    return @round(value);
}

Functiontrunc[src]

pub inline fn trunc(value: anytype) @TypeOf(value)

Rounds the given floating point number to an integer, towards zero. Uses a dedicated hardware instruction when available. This is the same as calling the builtin @trunc

Source Code

Source code
pub inline fn trunc(value: anytype) @TypeOf(value) {
    return @trunc(value);
}

Functionfloor[src]

pub inline fn floor(value: anytype) @TypeOf(value)

Returns the largest integral value not greater than the given floating point number. Uses a dedicated hardware instruction when available. This is the same as calling the builtin @floor

Source Code

Source code
pub inline fn floor(value: anytype) @TypeOf(value) {
    return @floor(value);
}

FunctionfloorPowerOfTwo[src]

pub fn floorPowerOfTwo(comptime T: type, value: T) T

Returns the nearest power of two less than or equal to value, or zero if value is less than or equal to zero.

Parameters

T: type
value: T

Example Usage

test floorPowerOfTwo {
    try testFloorPowerOfTwo();
    try comptime testFloorPowerOfTwo();
}

Source Code

Source code
pub fn floorPowerOfTwo(comptime T: type, value: T) T {
    const uT = std.meta.Int(.unsigned, @typeInfo(T).int.bits);
    if (value <= 0) return 0;
    return @as(T, 1) << log2_int(uT, @as(uT, @intCast(value)));
}

Functionceil[src]

pub inline fn ceil(value: anytype) @TypeOf(value)

Returns the smallest integral value not less than the given floating point number. Uses a dedicated hardware instruction when available. This is the same as calling the builtin @ceil

Source Code

Source code
pub inline fn ceil(value: anytype) @TypeOf(value) {
    return @ceil(value);
}

FunctionceilPowerOfTwoPromote[src]

pub fn ceilPowerOfTwoPromote(comptime T: type, value: T) std.meta.Int(@typeInfo(T).int.signedness, @typeInfo(T).int.bits + 1)

Returns the next power of two (if the value is not already a power of two). Only unsigned integers can be used. Zero is not an allowed input. Result is a type with 1 more bit than the input type.

Parameters

T: type
value: T

Example Usage

test ceilPowerOfTwoPromote {
    try testCeilPowerOfTwoPromote();
    try comptime testCeilPowerOfTwoPromote();
}

Source Code

Source code
pub fn ceilPowerOfTwoPromote(comptime T: type, value: T) std.meta.Int(@typeInfo(T).int.signedness, @typeInfo(T).int.bits + 1) {
    comptime assert(@typeInfo(T) == .int);
    comptime assert(@typeInfo(T).int.signedness == .unsigned);
    assert(value != 0);
    const PromotedType = std.meta.Int(@typeInfo(T).int.signedness, @typeInfo(T).int.bits + 1);
    const ShiftType = std.math.Log2Int(PromotedType);
    return @as(PromotedType, 1) << @as(ShiftType, @intCast(@typeInfo(T).int.bits - @clz(value - 1)));
}

FunctionceilPowerOfTwo[src]

pub fn ceilPowerOfTwo(comptime T: type, value: T) (error{Overflow}!T)

Returns the next power of two (if the value is not already a power of two). Only unsigned integers can be used. Zero is not an allowed input. If the value doesn't fit, returns an error.

Parameters

T: type
value: T

Example Usage

test ceilPowerOfTwo {
    try testCeilPowerOfTwo();
    try comptime testCeilPowerOfTwo();
}

Source Code

Source code
pub fn ceilPowerOfTwo(comptime T: type, value: T) (error{Overflow}!T) {
    comptime assert(@typeInfo(T) == .int);
    const info = @typeInfo(T).int;
    comptime assert(info.signedness == .unsigned);
    const PromotedType = std.meta.Int(info.signedness, info.bits + 1);
    const overflowBit = @as(PromotedType, 1) << info.bits;
    const x = ceilPowerOfTwoPromote(T, value);
    if (overflowBit & x != 0) {
        return error.Overflow;
    }
    return @as(T, @intCast(x));
}

FunctionceilPowerOfTwoAssert[src]

pub fn ceilPowerOfTwoAssert(comptime T: type, value: T) T

Returns the next power of two (if the value is not already a power of two). Only unsigned integers can be used. Zero is not an allowed input. Asserts that the value fits.

Parameters

T: type
value: T

Source Code

Source code
pub fn ceilPowerOfTwoAssert(comptime T: type, value: T) T {
    return ceilPowerOfTwo(T, value) catch unreachable;
}

Functionlog2_int[src]

pub fn log2_int(comptime T: type, x: T) Log2Int(T)

Return the log base 2 of integer value x, rounding down to the nearest integer.

Parameters

T: type
x: T

Source Code

Source code
pub fn log2_int(comptime T: type, x: T) Log2Int(T) {
    if (@typeInfo(T) != .int or @typeInfo(T).int.signedness != .unsigned)
        @compileError("log2_int requires an unsigned integer, found " ++ @typeName(T));
    assert(x != 0);
    return @as(Log2Int(T), @intCast(@typeInfo(T).int.bits - 1 - @clz(x)));
}

Functionlog2_int_ceil[src]

pub fn log2_int_ceil(comptime T: type, x: T) Log2IntCeil(T)

Return the log base 2 of integer value x, rounding up to the nearest integer.

Parameters

T: type
x: T

Example Usage

test log2_int_ceil {
    try testing.expect(log2_int_ceil(u32, 1) == 0);
    try testing.expect(log2_int_ceil(u32, 2) == 1);
    try testing.expect(log2_int_ceil(u32, 3) == 2);
    try testing.expect(log2_int_ceil(u32, 4) == 2);
    try testing.expect(log2_int_ceil(u32, 5) == 3);
    try testing.expect(log2_int_ceil(u32, 6) == 3);
    try testing.expect(log2_int_ceil(u32, 7) == 3);
    try testing.expect(log2_int_ceil(u32, 8) == 3);
    try testing.expect(log2_int_ceil(u32, 9) == 4);
    try testing.expect(log2_int_ceil(u32, 10) == 4);
}

Source Code

Source code
pub fn log2_int_ceil(comptime T: type, x: T) Log2IntCeil(T) {
    if (@typeInfo(T) != .int or @typeInfo(T).int.signedness != .unsigned)
        @compileError("log2_int_ceil requires an unsigned integer, found " ++ @typeName(T));
    assert(x != 0);
    if (x == 1) return 0;
    const log2_val: Log2IntCeil(T) = log2_int(T, x - 1);
    return log2_val + 1;
}

FunctionlossyCast[src]

pub fn lossyCast(comptime T: type, value: anytype) T

Cast a value to a different type. If the value doesn't fit in, or can't be perfectly represented by, the new type, it will be converted to the closest possible representation.

Parameters

T: type

Example Usage

test lossyCast {
    try testing.expect(lossyCast(i16, 70000.0) == @as(i16, 32767));
    try testing.expect(lossyCast(u32, @as(i16, -255)) == @as(u32, 0));
    try testing.expect(lossyCast(i9, @as(u32, 200)) == @as(i9, 200));
    try testing.expect(lossyCast(u32, @as(f32, maxInt(u32))) == maxInt(u32));
    try testing.expect(lossyCast(u32, nan(f32)) == 0);
}

Source Code

Source code
pub fn lossyCast(comptime T: type, value: anytype) T {
    switch (@typeInfo(T)) {
        .float => {
            switch (@typeInfo(@TypeOf(value))) {
                .int => return @floatFromInt(value),
                .float => return @floatCast(value),
                .comptime_int => return value,
                .comptime_float => return value,
                else => @compileError("bad type"),
            }
        },
        .int => {
            switch (@typeInfo(@TypeOf(value))) {
                .int, .comptime_int => {
                    if (value >= maxInt(T)) {
                        return maxInt(T);
                    } else if (value <= minInt(T)) {
                        return minInt(T);
                    } else {
                        return @intCast(value);
                    }
                },
                .float, .comptime_float => {
                    if (isNan(value)) {
                        return 0;
                    } else if (value >= maxInt(T)) {
                        return maxInt(T);
                    } else if (value <= minInt(T)) {
                        return minInt(T);
                    } else {
                        return @intFromFloat(value);
                    }
                },
                else => @compileError("bad type"),
            }
        },
        else => @compileError("bad result type"),
    }
}

Functionlerp[src]

pub fn lerp(a: anytype, b: anytype, t: anytype) @TypeOf(a, b, t)

Performs linear interpolation between a and b based on t. t ranges from 0.0 to 1.0, but may exceed these bounds. Supports floats and vectors of floats.

This does not guarantee returning b if t is 1 due to floating-point errors. This is monotonic.

Example Usage

test lerp {
    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/17884
    if (builtin.zig_backend == .stage2_x86_64 and
        !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .fma)) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/17884

    try testing.expectEqual(@as(f64, 75), lerp(50, 100, 0.5));
    try testing.expectEqual(@as(f32, 43.75), lerp(50, 25, 0.25));
    try testing.expectEqual(@as(f64, -31.25), lerp(-50, 25, 0.25));

    try testing.expectEqual(@as(f64, 30), lerp(10, 20, 2.0));
    try testing.expectEqual(@as(f64, 5), lerp(10, 20, -0.5));

    try testing.expectApproxEqRel(@as(f32, -7.16067345e+03), lerp(-10000.12345, -5000.12345, 0.56789), 1e-19);
    try testing.expectApproxEqRel(@as(f64, 7.010987590521e+62), lerp(0.123456789e-64, 0.123456789e64, 0.56789), 1e-33);

    try testing.expectEqual(@as(f32, 0.0), lerp(@as(f32, 1.0e8), 1.0, 1.0));
    try testing.expectEqual(@as(f64, 0.0), lerp(@as(f64, 1.0e16), 1.0, 1.0));
    try testing.expectEqual(@as(f32, 1.0), lerp(@as(f32, 1.0e7), 1.0, 1.0));
    try testing.expectEqual(@as(f64, 1.0), lerp(@as(f64, 1.0e15), 1.0, 1.0));

    {
        const a: @Vector(3, f32) = @splat(0);
        const b: @Vector(3, f32) = @splat(50);
        const t: @Vector(3, f32) = @splat(0.5);
        try testing.expectEqual(
            @Vector(3, f32){ 25, 25, 25 },
            lerp(a, b, t),
        );
    }
    {
        const a: @Vector(3, f64) = @splat(50);
        const b: @Vector(3, f64) = @splat(100);
        const t: @Vector(3, f64) = @splat(0.5);
        try testing.expectEqual(
            @Vector(3, f64){ 75, 75, 75 },
            lerp(a, b, t),
        );
    }
    {
        const a: @Vector(2, f32) = @splat(40);
        const b: @Vector(2, f32) = @splat(80);
        const t: @Vector(2, f32) = @Vector(2, f32){ 0.25, 0.75 };
        try testing.expectEqual(
            @Vector(2, f32){ 50, 70 },
            lerp(a, b, t),
        );
    }
}

Source Code

Source code
pub fn lerp(a: anytype, b: anytype, t: anytype) @TypeOf(a, b, t) {
    const Type = @TypeOf(a, b, t);
    return @mulAdd(Type, b - a, t, a);
}

FunctionmaxInt[src]

pub fn maxInt(comptime T: type) comptime_int

Returns the maximum value of integer type T.

Parameters

T: type

Example Usage

test maxInt {
    try testing.expect(maxInt(u0) == 0);
    try testing.expect(maxInt(u1) == 1);
    try testing.expect(maxInt(u8) == 255);
    try testing.expect(maxInt(u16) == 65535);
    try testing.expect(maxInt(u32) == 4294967295);
    try testing.expect(maxInt(u64) == 18446744073709551615);
    try testing.expect(maxInt(u128) == 340282366920938463463374607431768211455);

    try testing.expect(maxInt(i0) == 0);
    try testing.expect(maxInt(i1) == 0);
    try testing.expect(maxInt(i8) == 127);
    try testing.expect(maxInt(i16) == 32767);
    try testing.expect(maxInt(i32) == 2147483647);
    try testing.expect(maxInt(i63) == 4611686018427387903);
    try testing.expect(maxInt(i64) == 9223372036854775807);
    try testing.expect(maxInt(i128) == 170141183460469231731687303715884105727);
}

Source Code

Source code
pub fn maxInt(comptime T: type) comptime_int {
    const info = @typeInfo(T);
    const bit_count = info.int.bits;
    if (bit_count == 0) return 0;
    return (1 << (bit_count - @intFromBool(info.int.signedness == .signed))) - 1;
}

FunctionminInt[src]

pub fn minInt(comptime T: type) comptime_int

Returns the minimum value of integer type T.

Parameters

T: type

Example Usage

test minInt {
    try testing.expect(minInt(u0) == 0);
    try testing.expect(minInt(u1) == 0);
    try testing.expect(minInt(u8) == 0);
    try testing.expect(minInt(u16) == 0);
    try testing.expect(minInt(u32) == 0);
    try testing.expect(minInt(u63) == 0);
    try testing.expect(minInt(u64) == 0);
    try testing.expect(minInt(u128) == 0);

    try testing.expect(minInt(i0) == 0);
    try testing.expect(minInt(i1) == -1);
    try testing.expect(minInt(i8) == -128);
    try testing.expect(minInt(i16) == -32768);
    try testing.expect(minInt(i32) == -2147483648);
    try testing.expect(minInt(i63) == -4611686018427387904);
    try testing.expect(minInt(i64) == -9223372036854775808);
    try testing.expect(minInt(i128) == -170141183460469231731687303715884105728);
}

Source Code

Source code
pub fn minInt(comptime T: type) comptime_int {
    const info = @typeInfo(T);
    const bit_count = info.int.bits;
    if (info.int.signedness == .unsigned) return 0;
    if (bit_count == 0) return 0;
    return -(1 << (bit_count - 1));
}

FunctionmulWide[src]

pub fn mulWide(comptime T: type, a: T, b: T) std.meta.Int( @typeInfo(T).int.signedness, @typeInfo(T).int.bits * 2, )

Multiply a and b. Return type is wide enough to guarantee no overflow.

Parameters

T: type
a: T
b: T

Example Usage

test mulWide {
    try testing.expect(mulWide(u8, 5, 5) == 25);
    try testing.expect(mulWide(i8, 5, -5) == -25);
    try testing.expect(mulWide(u8, 100, 100) == 10000);
}

Source Code

Source code
pub fn mulWide(comptime T: type, a: T, b: T) std.meta.Int(
    @typeInfo(T).int.signedness,
    @typeInfo(T).int.bits * 2,
) {
    const ResultInt = std.meta.Int(
        @typeInfo(T).int.signedness,
        @typeInfo(T).int.bits * 2,
    );
    return @as(ResultInt, a) * @as(ResultInt, b);
}

Functionorder[src]

pub fn order(a: anytype, b: anytype) Order

Given two numbers, this function returns the order they are with respect to each other.

Example Usage

test order {
    try testing.expect(order(0, 0) == .eq);
    try testing.expect(order(1, 0) == .gt);
    try testing.expect(order(-1, 0) == .lt);
}

Source Code

Source code
pub fn order(a: anytype, b: anytype) Order {
    if (a == b) {
        return .eq;
    } else if (a < b) {
        return .lt;
    } else if (a > b) {
        return .gt;
    } else {
        unreachable;
    }
}

Functioncompare[src]

pub fn compare(a: anytype, op: CompareOperator, b: anytype) bool

This function does the same thing as comparison operators, however the operator is a runtime-known enum value. Works on any operands that support comparison operators.

Parameters

Example Usage

test compare {
    try testing.expect(compare(@as(i8, -1), .lt, @as(u8, 255)));
    try testing.expect(compare(@as(i8, 2), .gt, @as(u8, 1)));
    try testing.expect(!compare(@as(i8, -1), .gte, @as(u8, 255)));
    try testing.expect(compare(@as(u8, 255), .gt, @as(i8, -1)));
    try testing.expect(!compare(@as(u8, 255), .lte, @as(i8, -1)));
    try testing.expect(compare(@as(i8, -1), .lt, @as(u9, 255)));
    try testing.expect(!compare(@as(i8, -1), .gte, @as(u9, 255)));
    try testing.expect(compare(@as(u9, 255), .gt, @as(i8, -1)));
    try testing.expect(!compare(@as(u9, 255), .lte, @as(i8, -1)));
    try testing.expect(compare(@as(i9, -1), .lt, @as(u8, 255)));
    try testing.expect(!compare(@as(i9, -1), .gte, @as(u8, 255)));
    try testing.expect(compare(@as(u8, 255), .gt, @as(i9, -1)));
    try testing.expect(!compare(@as(u8, 255), .lte, @as(i9, -1)));
    try testing.expect(compare(@as(u8, 1), .lt, @as(u8, 2)));
    try testing.expect(@as(u8, @bitCast(@as(i8, -1))) == @as(u8, 255));
    try testing.expect(!compare(@as(u8, 255), .eq, @as(i8, -1)));
    try testing.expect(compare(@as(u8, 1), .eq, @as(u8, 1)));
}

Source Code

Source code
pub fn compare(a: anytype, op: CompareOperator, b: anytype) bool {
    return switch (op) {
        .lt => a < b,
        .lte => a <= b,
        .eq => a == b,
        .neq => a != b,
        .gt => a > b,
        .gte => a >= b,
    };
}

FunctionboolMask[src]

pub inline fn boolMask(comptime MaskInt: type, value: bool) MaskInt

Returns a mask of all ones if value is true, and a mask of all zeroes if value is false. Compiles to one instruction for register sized integers.

Parameters

MaskInt: type
value: bool

Example Usage

test boolMask {
    const runTest = struct {
        fn runTest() !void {
            try testing.expectEqual(@as(u1, 0), boolMask(u1, false));
            try testing.expectEqual(@as(u1, 1), boolMask(u1, true));

            try testing.expectEqual(@as(i1, 0), boolMask(i1, false));
            try testing.expectEqual(@as(i1, -1), boolMask(i1, true));

            try testing.expectEqual(@as(u13, 0), boolMask(u13, false));
            try testing.expectEqual(@as(u13, 0x1FFF), boolMask(u13, true));

            try testing.expectEqual(@as(i13, 0), boolMask(i13, false));
            try testing.expectEqual(@as(i13, -1), boolMask(i13, true));

            try testing.expectEqual(@as(u32, 0), boolMask(u32, false));
            try testing.expectEqual(@as(u32, 0xFFFF_FFFF), boolMask(u32, true));

            try testing.expectEqual(@as(i32, 0), boolMask(i32, false));
            try testing.expectEqual(@as(i32, -1), boolMask(i32, true));
        }
    }.runTest;
    try runTest();
    try comptime runTest();
}

Source Code

Source code
pub inline fn boolMask(comptime MaskInt: type, value: bool) MaskInt {
    if (@typeInfo(MaskInt) != .int)
        @compileError("boolMask requires an integer mask type.");

    if (MaskInt == u0 or MaskInt == i0)
        @compileError("boolMask cannot convert to u0 or i0, they are too small.");

    // The u1 and i1 cases tend to overflow,
    // so we special case them here.
    if (MaskInt == u1) return @intFromBool(value);
    if (MaskInt == i1) {
        // The @as here is a workaround for #7950
        return @as(i1, @bitCast(@as(u1, @intFromBool(value))));
    }

    return -%@as(MaskInt, @intCast(@intFromBool(value)));
}

FunctioncomptimeMod[src]

pub fn comptimeMod(num: anytype, comptime denom: comptime_int) IntFittingRange(0, denom - 1)

Return the mod of num with the smallest integer type

Parameters

denom: comptime_int

Source Code

Source code
pub fn comptimeMod(num: anytype, comptime denom: comptime_int) IntFittingRange(0, denom - 1) {
    return @as(IntFittingRange(0, denom - 1), @intCast(@mod(num, denom)));
}

Functionsign[src]

pub inline fn sign(i: anytype) @TypeOf(i)

Returns -1, 0, or 1. Supports integer and float types and vectors of integer and float types. Unsigned integer types will always return 0 or 1. Branchless.

Example Usage

test sign {
    try testSign();
    try comptime testSign();
}

Source Code

Source code
pub inline fn sign(i: anytype) @TypeOf(i) {
    const T = @TypeOf(i);
    return switch (@typeInfo(T)) {
        .int, .comptime_int => @as(T, @intFromBool(i > 0)) - @as(T, @intFromBool(i < 0)),
        .float, .comptime_float => @as(T, @floatFromInt(@intFromBool(i > 0))) - @as(T, @floatFromInt(@intFromBool(i < 0))),
        .vector => |vinfo| blk: {
            switch (@typeInfo(vinfo.child)) {
                .int, .float => {
                    const zero: T = @splat(0);
                    const one: T = @splat(1);
                    break :blk @select(vinfo.child, i > zero, one, zero) - @select(vinfo.child, i < zero, one, zero);
                },
                else => @compileError("Expected vector of ints or floats, found " ++ @typeName(T)),
            }
        },
        else => @compileError("Expected an int, float or vector of one, found " ++ @typeName(T)),
    };
}

Source Code

Source code
const builtin = @import("builtin");
const std = @import("std.zig");
const float = @import("math/float.zig");
const assert = std.debug.assert;
const mem = std.mem;
const testing = std.testing;

/// Euler's number (e)
pub const e = 2.71828182845904523536028747135266249775724709369995;

/// Archimedes' constant (π)
pub const pi = 3.14159265358979323846264338327950288419716939937510;

/// Phi or Golden ratio constant (Φ) = (1 + sqrt(5))/2
pub const phi = 1.6180339887498948482045868343656381177203091798057628621;

/// Circle constant (τ)
pub const tau = 2 * pi;

/// log2(e)
pub const log2e = 1.442695040888963407359924681001892137;

/// log10(e)
pub const log10e = 0.434294481903251827651128918916605082;

/// ln(2)
pub const ln2 = 0.693147180559945309417232121458176568;

/// ln(10)
pub const ln10 = 2.302585092994045684017991454684364208;

/// 2/sqrt(π)
pub const two_sqrtpi = 1.128379167095512573896158903121545172;

/// sqrt(2)
pub const sqrt2 = 1.414213562373095048801688724209698079;

/// 1/sqrt(2)
pub const sqrt1_2 = 0.707106781186547524400844362104849039;

/// pi/180.0
pub const rad_per_deg = 0.0174532925199432957692369076848861271344287188854172545609719144;

/// 180.0/pi
pub const deg_per_rad = 57.295779513082320876798154814105170332405472466564321549160243861;

pub const floatExponentBits = float.floatExponentBits;
pub const floatMantissaBits = float.floatMantissaBits;
pub const floatFractionalBits = float.floatFractionalBits;
pub const floatExponentMin = float.floatExponentMin;
pub const floatExponentMax = float.floatExponentMax;
pub const floatTrueMin = float.floatTrueMin;
pub const floatMin = float.floatMin;
pub const floatMax = float.floatMax;
pub const floatEps = float.floatEps;
pub const floatEpsAt = float.floatEpsAt;
pub const inf = float.inf;
pub const nan = float.nan;
pub const snan = float.snan;

/// Performs an approximate comparison of two floating point values `x` and `y`.
/// Returns true if the absolute difference between them is less or equal than
/// the specified tolerance.
///
/// The `tolerance` parameter is the absolute tolerance used when determining if
/// the two numbers are close enough; a good value for this parameter is a small
/// multiple of `floatEps(T)`.
///
/// Note that this function is recommended for comparing small numbers
/// around zero; using `approxEqRel` is suggested otherwise.
///
/// NaN values are never considered equal to any value.
pub fn approxEqAbs(comptime T: type, x: T, y: T, tolerance: T) bool {
    assert(@typeInfo(T) == .float or @typeInfo(T) == .comptime_float);
    assert(tolerance >= 0);

    // Fast path for equal values (and signed zeros and infinites).
    if (x == y)
        return true;

    if (isNan(x) or isNan(y))
        return false;

    return @abs(x - y) <= tolerance;
}

/// Performs an approximate comparison of two floating point values `x` and `y`.
/// Returns true if the absolute difference between them is less or equal than
/// `max(|x|, |y|) * tolerance`, where `tolerance` is a positive number greater
/// than zero.
///
/// The `tolerance` parameter is the relative tolerance used when determining if
/// the two numbers are close enough; a good value for this parameter is usually
/// `sqrt(floatEps(T))`, meaning that the two numbers are considered equal if at
/// least half of the digits are equal.
///
/// Note that for comparisons of small numbers around zero this function won't
/// give meaningful results, use `approxEqAbs` instead.
///
/// NaN values are never considered equal to any value.
pub fn approxEqRel(comptime T: type, x: T, y: T, tolerance: T) bool {
    assert(@typeInfo(T) == .float or @typeInfo(T) == .comptime_float);
    assert(tolerance > 0);

    // Fast path for equal values (and signed zeros and infinites).
    if (x == y)
        return true;

    if (isNan(x) or isNan(y))
        return false;

    return @abs(x - y) <= @max(@abs(x), @abs(y)) * tolerance;
}

test approxEqAbs {
    inline for ([_]type{ f16, f32, f64, f128 }) |T| {
        const eps_value = comptime floatEps(T);
        const min_value = comptime floatMin(T);

        try testing.expect(approxEqAbs(T, 0.0, 0.0, eps_value));
        try testing.expect(approxEqAbs(T, -0.0, -0.0, eps_value));
        try testing.expect(approxEqAbs(T, 0.0, -0.0, eps_value));
        try testing.expect(!approxEqAbs(T, 1.0 + 2 * eps_value, 1.0, eps_value));
        try testing.expect(approxEqAbs(T, 1.0 + 1 * eps_value, 1.0, eps_value));
        try testing.expect(approxEqAbs(T, min_value, 0.0, eps_value * 2));
        try testing.expect(approxEqAbs(T, -min_value, 0.0, eps_value * 2));
    }

    comptime {
        // `comptime_float` is guaranteed to have the same precision and operations of
        // the largest other floating point type, which is f128 but it doesn't have a
        // defined layout so we can't rely on `@bitCast` to construct the smallest
        // possible epsilon value like we do in the tests above. In the same vein, we
        // also can't represent a max/min, `NaN` or `Inf` values.
        const eps_value = 1e-4;

        try testing.expect(approxEqAbs(comptime_float, 0.0, 0.0, eps_value));
        try testing.expect(approxEqAbs(comptime_float, -0.0, -0.0, eps_value));
        try testing.expect(approxEqAbs(comptime_float, 0.0, -0.0, eps_value));
        try testing.expect(!approxEqAbs(comptime_float, 1.0 + 2 * eps_value, 1.0, eps_value));
        try testing.expect(approxEqAbs(comptime_float, 1.0 + 1 * eps_value, 1.0, eps_value));
    }
}

test approxEqRel {
    inline for ([_]type{ f16, f32, f64, f128 }) |T| {
        const eps_value = comptime floatEps(T);
        const sqrt_eps_value = comptime sqrt(eps_value);
        const nan_value = comptime nan(T);
        const inf_value = comptime inf(T);
        const min_value = comptime floatMin(T);

        try testing.expect(approxEqRel(T, 1.0, 1.0, sqrt_eps_value));
        try testing.expect(!approxEqRel(T, 1.0, 0.0, sqrt_eps_value));
        try testing.expect(!approxEqRel(T, 1.0, nan_value, sqrt_eps_value));
        try testing.expect(!approxEqRel(T, nan_value, nan_value, sqrt_eps_value));
        try testing.expect(approxEqRel(T, inf_value, inf_value, sqrt_eps_value));
        try testing.expect(approxEqRel(T, min_value, min_value, sqrt_eps_value));
        try testing.expect(approxEqRel(T, -min_value, -min_value, sqrt_eps_value));
    }

    comptime {
        // `comptime_float` is guaranteed to have the same precision and operations of
        // the largest other floating point type, which is f128 but it doesn't have a
        // defined layout so we can't rely on `@bitCast` to construct the smallest
        // possible epsilon value like we do in the tests above. In the same vein, we
        // also can't represent a max/min, `NaN` or `Inf` values.
        const eps_value = 1e-4;
        const sqrt_eps_value = sqrt(eps_value);

        try testing.expect(approxEqRel(comptime_float, 1.0, 1.0, sqrt_eps_value));
        try testing.expect(!approxEqRel(comptime_float, 1.0, 0.0, sqrt_eps_value));
    }
}

pub fn raiseInvalid() void {
    // Raise INVALID fpu exception
}

pub fn raiseUnderflow() void {
    // Raise UNDERFLOW fpu exception
}

pub fn raiseOverflow() void {
    // Raise OVERFLOW fpu exception
}

pub fn raiseInexact() void {
    // Raise INEXACT fpu exception
}

pub fn raiseDivByZero() void {
    // Raise INEXACT fpu exception
}

pub const isNan = @import("math/isnan.zig").isNan;
pub const isSignalNan = @import("math/isnan.zig").isSignalNan;
pub const frexp = @import("math/frexp.zig").frexp;
pub const Frexp = @import("math/frexp.zig").Frexp;
pub const modf = @import("math/modf.zig").modf;
pub const Modf = @import("math/modf.zig").Modf;
pub const copysign = @import("math/copysign.zig").copysign;
pub const isFinite = @import("math/isfinite.zig").isFinite;
pub const isInf = @import("math/isinf.zig").isInf;
pub const isPositiveInf = @import("math/isinf.zig").isPositiveInf;
pub const isNegativeInf = @import("math/isinf.zig").isNegativeInf;
pub const isPositiveZero = @import("math/iszero.zig").isPositiveZero;
pub const isNegativeZero = @import("math/iszero.zig").isNegativeZero;
pub const isNormal = @import("math/isnormal.zig").isNormal;
pub const nextAfter = @import("math/nextafter.zig").nextAfter;
pub const signbit = @import("math/signbit.zig").signbit;
pub const scalbn = @import("math/scalbn.zig").scalbn;
pub const ldexp = @import("math/ldexp.zig").ldexp;
pub const pow = @import("math/pow.zig").pow;
pub const powi = @import("math/powi.zig").powi;
pub const sqrt = @import("math/sqrt.zig").sqrt;
pub const cbrt = @import("math/cbrt.zig").cbrt;
pub const acos = @import("math/acos.zig").acos;
pub const asin = @import("math/asin.zig").asin;
pub const atan = @import("math/atan.zig").atan;
pub const atan2 = @import("math/atan2.zig").atan2;
pub const hypot = @import("math/hypot.zig").hypot;
pub const expm1 = @import("math/expm1.zig").expm1;
pub const ilogb = @import("math/ilogb.zig").ilogb;
pub const log = @import("math/log.zig").log;
pub const log2 = @import("math/log2.zig").log2;
pub const log10 = @import("math/log10.zig").log10;
pub const log10_int = @import("math/log10.zig").log10_int;
pub const log_int = @import("math/log_int.zig").log_int;
pub const log1p = @import("math/log1p.zig").log1p;
pub const asinh = @import("math/asinh.zig").asinh;
pub const acosh = @import("math/acosh.zig").acosh;
pub const atanh = @import("math/atanh.zig").atanh;
pub const sinh = @import("math/sinh.zig").sinh;
pub const cosh = @import("math/cosh.zig").cosh;
pub const tanh = @import("math/tanh.zig").tanh;
pub const gcd = @import("math/gcd.zig").gcd;
pub const gamma = @import("math/gamma.zig").gamma;
pub const lgamma = @import("math/gamma.zig").lgamma;

/// Sine trigonometric function on a floating point number.
/// Uses a dedicated hardware instruction when available.
/// This is the same as calling the builtin @sin
pub inline fn sin(value: anytype) @TypeOf(value) {
    return @sin(value);
}

/// Cosine trigonometric function on a floating point number.
/// Uses a dedicated hardware instruction when available.
/// This is the same as calling the builtin @cos
pub inline fn cos(value: anytype) @TypeOf(value) {
    return @cos(value);
}

/// Tangent trigonometric function on a floating point number.
/// Uses a dedicated hardware instruction when available.
/// This is the same as calling the builtin @tan
pub inline fn tan(value: anytype) @TypeOf(value) {
    return @tan(value);
}

/// Converts an angle in radians to degrees. T must be a float or comptime number or a vector of floats.
pub fn radiansToDegrees(ang: anytype) if (@TypeOf(ang) == comptime_int) comptime_float else @TypeOf(ang) {
    const T = @TypeOf(ang);
    switch (@typeInfo(T)) {
        .float, .comptime_float, .comptime_int => return ang * deg_per_rad,
        .vector => |V| if (@typeInfo(V.child) == .float) return ang * @as(T, @splat(deg_per_rad)),
        else => {},
    }
    @compileError("Input must be float or a comptime number, or a vector of floats.");
}

test radiansToDegrees {
    const zero: f32 = 0;
    const half_pi: f32 = pi / 2.0;
    const neg_quart_pi: f32 = -pi / 4.0;
    const one_pi: f32 = pi;
    const two_pi: f32 = 2.0 * pi;
    try std.testing.expectApproxEqAbs(@as(f32, 0), radiansToDegrees(zero), 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 90), radiansToDegrees(half_pi), 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, -45), radiansToDegrees(neg_quart_pi), 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 180), radiansToDegrees(one_pi), 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 360), radiansToDegrees(two_pi), 1e-6);

    const result = radiansToDegrees(@Vector(4, f32){
        half_pi,
        neg_quart_pi,
        one_pi,
        two_pi,
    });
    try std.testing.expectApproxEqAbs(@as(f32, 90), result[0], 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, -45), result[1], 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 180), result[2], 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 360), result[3], 1e-6);
}

/// Converts an angle in degrees to radians. T must be a float or comptime number or a vector of floats.
pub fn degreesToRadians(ang: anytype) if (@TypeOf(ang) == comptime_int) comptime_float else @TypeOf(ang) {
    const T = @TypeOf(ang);
    switch (@typeInfo(T)) {
        .float, .comptime_float, .comptime_int => return ang * rad_per_deg,
        .vector => |V| if (@typeInfo(V.child) == .float) return ang * @as(T, @splat(rad_per_deg)),
        else => {},
    }
    @compileError("Input must be float or a comptime number, or a vector of floats.");
}

test degreesToRadians {
    const ninety: f32 = 90;
    const neg_two_seventy: f32 = -270;
    const three_sixty: f32 = 360;
    try std.testing.expectApproxEqAbs(@as(f32, pi / 2.0), degreesToRadians(ninety), 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, -3 * pi / 2.0), degreesToRadians(neg_two_seventy), 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 2 * pi), degreesToRadians(three_sixty), 1e-6);

    const result = degreesToRadians(@Vector(3, f32){
        ninety,
        neg_two_seventy,
        three_sixty,
    });
    try std.testing.expectApproxEqAbs(@as(f32, pi / 2.0), result[0], 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, -3 * pi / 2.0), result[1], 1e-6);
    try std.testing.expectApproxEqAbs(@as(f32, 2 * pi), result[2], 1e-6);
}

/// Base-e exponential function on a floating point number.
/// Uses a dedicated hardware instruction when available.
/// This is the same as calling the builtin @exp
pub inline fn exp(value: anytype) @TypeOf(value) {
    return @exp(value);
}

/// Base-2 exponential function on a floating point number.
/// Uses a dedicated hardware instruction when available.
/// This is the same as calling the builtin @exp2
pub inline fn exp2(value: anytype) @TypeOf(value) {
    return @exp2(value);
}

pub const complex = @import("math/complex.zig");
pub const Complex = complex.Complex;

pub const big = @import("math/big.zig");

test {
    _ = floatExponentBits;
    _ = floatMantissaBits;
    _ = floatFractionalBits;
    _ = floatExponentMin;
    _ = floatExponentMax;
    _ = floatTrueMin;
    _ = floatMin;
    _ = floatMax;
    _ = floatEps;
    _ = inf;
    _ = nan;
    _ = snan;
    _ = isNan;
    _ = isSignalNan;
    _ = frexp;
    _ = Frexp;
    _ = modf;
    _ = Modf;
    _ = copysign;
    _ = isFinite;
    _ = isInf;
    _ = isPositiveInf;
    _ = isNegativeInf;
    _ = isNormal;
    _ = nextAfter;
    _ = signbit;
    _ = scalbn;
    _ = ldexp;
    _ = pow;
    _ = powi;
    _ = sqrt;
    _ = cbrt;
    _ = acos;
    _ = asin;
    _ = atan;
    _ = atan2;
    _ = hypot;
    _ = expm1;
    _ = ilogb;
    _ = log;
    _ = log2;
    _ = log10;
    _ = log10_int;
    _ = log_int;
    _ = log1p;
    _ = asinh;
    _ = acosh;
    _ = atanh;
    _ = sinh;
    _ = cosh;
    _ = tanh;
    _ = gcd;
    _ = gamma;
    _ = lgamma;

    _ = complex;
    _ = Complex;

    _ = big;
}

/// Given two types, returns the smallest one which is capable of holding the
/// full range of the minimum value.
pub fn Min(comptime A: type, comptime B: type) type {
    switch (@typeInfo(A)) {
        .int => |a_info| switch (@typeInfo(B)) {
            .int => |b_info| if (a_info.signedness == .unsigned and b_info.signedness == .unsigned) {
                if (a_info.bits < b_info.bits) {
                    return A;
                } else {
                    return B;
                }
            },
            else => {},
        },
        else => {},
    }
    return @TypeOf(@as(A, 0) + @as(B, 0));
}

/// Odd sawtooth function
/// ```
///         |
///      /  | /    /
///     /   |/    /
///  --/----/----/--
///   /    /|   /
///  /    / |  /
///         |
/// ```
/// Limit x to the half-open interval [-r, r).
pub fn wrap(x: anytype, r: anytype) @TypeOf(x) {
    const info_x = @typeInfo(@TypeOf(x));
    const info_r = @typeInfo(@TypeOf(r));
    if (info_x == .int and info_x.int.signedness != .signed) {
        @compileError("x must be floating point, comptime integer, or signed integer.");
    }
    switch (info_r) {
        .int => {
            // in the rare usecase of r not being comptime_int or float,
            // take the penalty of having an intermediary type conversion,
            // otherwise the alternative is to unwind iteratively to avoid overflow
            const R = comptime do: {
                var info = info_r;
                info.int.bits += 1;
                info.int.signedness = .signed;
                break :do @Type(info);
            };
            const radius: if (info_r.int.signedness == .signed) @TypeOf(r) else R = r;
            return @intCast(@mod(x - radius, 2 * @as(R, r)) - r); // provably impossible to overflow
        },
        else => {
            return @mod(x - r, 2 * r) - r;
        },
    }
}
test wrap {
    // Within range
    try testing.expect(wrap(@as(i32, -75), @as(i32, 180)) == -75);
    try testing.expect(wrap(@as(i32, -75), @as(i32, -180)) == -75);
    // Below
    try testing.expect(wrap(@as(i32, -225), @as(i32, 180)) == 135);
    try testing.expect(wrap(@as(i32, -225), @as(i32, -180)) == 135);
    // Above
    try testing.expect(wrap(@as(i32, 361), @as(i32, 180)) == 1);
    try testing.expect(wrap(@as(i32, 361), @as(i32, -180)) == 1);

    // One period, right limit, positive r
    try testing.expect(wrap(@as(i32, 180), @as(i32, 180)) == -180);
    // One period, left limit, positive r
    try testing.expect(wrap(@as(i32, -180), @as(i32, 180)) == -180);
    // One period, right limit, negative r
    try testing.expect(wrap(@as(i32, 180), @as(i32, -180)) == 180);
    // One period, left limit, negative r
    try testing.expect(wrap(@as(i32, -180), @as(i32, -180)) == 180);

    // Two periods, right limit, positive r
    try testing.expect(wrap(@as(i32, 540), @as(i32, 180)) == -180);
    // Two periods, left limit, positive r
    try testing.expect(wrap(@as(i32, -540), @as(i32, 180)) == -180);
    // Two periods, right limit, negative r
    try testing.expect(wrap(@as(i32, 540), @as(i32, -180)) == 180);
    // Two periods, left limit, negative r
    try testing.expect(wrap(@as(i32, -540), @as(i32, -180)) == 180);

    // Floating point
    try testing.expect(wrap(@as(f32, 1.125), @as(f32, 1.0)) == -0.875);
    try testing.expect(wrap(@as(f32, -127.5), @as(f32, 180)) == -127.5);

    // Mix of comptime and non-comptime
    var i: i32 = 1;
    _ = &i;
    try testing.expect(wrap(i, 10) == 1);

    const limit: i32 = 180;
    // Within range
    try testing.expect(wrap(@as(i32, -75), limit) == -75);
    // Below
    try testing.expect(wrap(@as(i32, -225), limit) == 135);
    // Above
    try testing.expect(wrap(@as(i32, 361), limit) == 1);
}

/// Odd ramp function
/// ```
///         |  _____
///         | /
///         |/
///  -------/-------
///        /|
///  _____/ |
///         |
/// ```
/// Limit val to the inclusive range [lower, upper].
pub fn clamp(val: anytype, lower: anytype, upper: anytype) @TypeOf(val, lower, upper) {
    const T = @TypeOf(val, lower, upper);
    switch (@typeInfo(T)) {
        .int, .float, .comptime_int, .comptime_float => assert(lower <= upper),
        .vector => |vinfo| switch (@typeInfo(vinfo.child)) {
            .int, .float => assert(@reduce(.And, lower <= upper)),
            else => @compileError("Expected vector of ints or floats, found " ++ @typeName(T)),
        },
        else => @compileError("Expected an int, float or vector of one, found " ++ @typeName(T)),
    }
    return @max(lower, @min(val, upper));
}
test clamp {
    // Within range
    try testing.expect(std.math.clamp(@as(i32, -1), @as(i32, -4), @as(i32, 7)) == -1);
    // Below
    try testing.expect(std.math.clamp(@as(i32, -5), @as(i32, -4), @as(i32, 7)) == -4);
    // Above
    try testing.expect(std.math.clamp(@as(i32, 8), @as(i32, -4), @as(i32, 7)) == 7);

    // Floating point
    try testing.expect(std.math.clamp(@as(f32, 1.1), @as(f32, 0.0), @as(f32, 1.0)) == 1.0);
    try testing.expect(std.math.clamp(@as(f32, -127.5), @as(f32, -200), @as(f32, -100)) == -127.5);

    // Vector
    try testing.expect(@reduce(.And, std.math.clamp(@as(@Vector(3, f32), .{ 1.4, 15.23, 28.3 }), @as(@Vector(3, f32), .{ 9.8, 13.2, 15.6 }), @as(@Vector(3, f32), .{ 15.2, 22.8, 26.3 })) == @as(@Vector(3, f32), .{ 9.8, 15.23, 26.3 })));

    // Mix of comptime and non-comptime
    var i: i32 = 1;
    _ = &i;
    try testing.expect(std.math.clamp(i, 0, 1) == 1);
}

/// Returns the product of a and b. Returns an error on overflow.
pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) {
    if (T == comptime_int) return a * b;
    const ov = @mulWithOverflow(a, b);
    if (ov[1] != 0) return error.Overflow;
    return ov[0];
}

/// Returns the sum of a and b. Returns an error on overflow.
pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) {
    if (T == comptime_int) return a + b;
    const ov = @addWithOverflow(a, b);
    if (ov[1] != 0) return error.Overflow;
    return ov[0];
}

/// Returns a - b, or an error on overflow.
pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) {
    if (T == comptime_int) return a - b;
    const ov = @subWithOverflow(a, b);
    if (ov[1] != 0) return error.Overflow;
    return ov[0];
}

pub fn negate(x: anytype) !@TypeOf(x) {
    return sub(@TypeOf(x), 0, x);
}

/// Shifts a left by shift_amt. Returns an error on overflow. shift_amt
/// is unsigned.
pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {
    if (T == comptime_int) return a << shift_amt;
    const ov = @shlWithOverflow(a, shift_amt);
    if (ov[1] != 0) return error.Overflow;
    return ov[0];
}

/// Shifts left. Overflowed bits are truncated.
/// A negative shift amount results in a right shift.
pub fn shl(comptime T: type, a: T, shift_amt: anytype) T {
    const abs_shift_amt = @abs(shift_amt);

    const casted_shift_amt = blk: {
        if (@typeInfo(T) == .vector) {
            const C = @typeInfo(T).vector.child;
            const len = @typeInfo(T).vector.len;
            if (abs_shift_amt >= @typeInfo(C).int.bits) return @splat(0);
            break :blk @as(@Vector(len, Log2Int(C)), @splat(@as(Log2Int(C), @intCast(abs_shift_amt))));
        } else {
            if (abs_shift_amt >= @typeInfo(T).int.bits) return 0;
            break :blk @as(Log2Int(T), @intCast(abs_shift_amt));
        }
    };

    if (@TypeOf(shift_amt) == comptime_int or @typeInfo(@TypeOf(shift_amt)).int.signedness == .signed) {
        if (shift_amt < 0) {
            return a >> casted_shift_amt;
        }
    }

    return a << casted_shift_amt;
}

test shl {
    try testing.expect(shl(u8, 0b11111111, @as(usize, 3)) == 0b11111000);
    try testing.expect(shl(u8, 0b11111111, @as(usize, 8)) == 0);
    try testing.expect(shl(u8, 0b11111111, @as(usize, 9)) == 0);
    try testing.expect(shl(u8, 0b11111111, @as(isize, -2)) == 0b00111111);
    try testing.expect(shl(u8, 0b11111111, 3) == 0b11111000);
    try testing.expect(shl(u8, 0b11111111, 8) == 0);
    try testing.expect(shl(u8, 0b11111111, 9) == 0);
    try testing.expect(shl(u8, 0b11111111, -2) == 0b00111111);
    try testing.expect(shl(@Vector(1, u32), @Vector(1, u32){42}, @as(usize, 1))[0] == @as(u32, 42) << 1);
    try testing.expect(shl(@Vector(1, u32), @Vector(1, u32){42}, @as(isize, -1))[0] == @as(u32, 42) >> 1);
    try testing.expect(shl(@Vector(1, u32), @Vector(1, u32){42}, 33)[0] == 0);
}

/// Shifts right. Overflowed bits are truncated.
/// A negative shift amount results in a left shift.
pub fn shr(comptime T: type, a: T, shift_amt: anytype) T {
    const abs_shift_amt = @abs(shift_amt);

    const casted_shift_amt = blk: {
        if (@typeInfo(T) == .vector) {
            const C = @typeInfo(T).vector.child;
            const len = @typeInfo(T).vector.len;
            if (abs_shift_amt >= @typeInfo(C).int.bits) return @splat(0);
            break :blk @as(@Vector(len, Log2Int(C)), @splat(@as(Log2Int(C), @intCast(abs_shift_amt))));
        } else {
            if (abs_shift_amt >= @typeInfo(T).int.bits) return 0;
            break :blk @as(Log2Int(T), @intCast(abs_shift_amt));
        }
    };

    if (@TypeOf(shift_amt) == comptime_int or @typeInfo(@TypeOf(shift_amt)).int.signedness == .signed) {
        if (shift_amt < 0) {
            return a << casted_shift_amt;
        }
    }

    return a >> casted_shift_amt;
}

test shr {
    try testing.expect(shr(u8, 0b11111111, @as(usize, 3)) == 0b00011111);
    try testing.expect(shr(u8, 0b11111111, @as(usize, 8)) == 0);
    try testing.expect(shr(u8, 0b11111111, @as(usize, 9)) == 0);
    try testing.expect(shr(u8, 0b11111111, @as(isize, -2)) == 0b11111100);
    try testing.expect(shr(u8, 0b11111111, 3) == 0b00011111);
    try testing.expect(shr(u8, 0b11111111, 8) == 0);
    try testing.expect(shr(u8, 0b11111111, 9) == 0);
    try testing.expect(shr(u8, 0b11111111, -2) == 0b11111100);
    try testing.expect(shr(@Vector(1, u32), @Vector(1, u32){42}, @as(usize, 1))[0] == @as(u32, 42) >> 1);
    try testing.expect(shr(@Vector(1, u32), @Vector(1, u32){42}, @as(isize, -1))[0] == @as(u32, 42) << 1);
    try testing.expect(shr(@Vector(1, u32), @Vector(1, u32){42}, 33)[0] == 0);
}

/// Rotates right. Only unsigned values can be rotated.  Negative shift
/// values result in shift modulo the bit count.
pub fn rotr(comptime T: type, x: T, r: anytype) T {
    if (@typeInfo(T) == .vector) {
        const C = @typeInfo(T).vector.child;
        if (C == u0) return 0;

        if (@typeInfo(C).int.signedness == .signed) {
            @compileError("cannot rotate signed integers");
        }
        const ar: Log2Int(C) = @intCast(@mod(r, @typeInfo(C).int.bits));
        return (x >> @splat(ar)) | (x << @splat(1 + ~ar));
    } else if (@typeInfo(T).int.signedness == .signed) {
        @compileError("cannot rotate signed integer");
    } else {
        if (T == u0) return 0;

        if (comptime isPowerOfTwo(@typeInfo(T).int.bits)) {
            const ar: Log2Int(T) = @intCast(@mod(r, @typeInfo(T).int.bits));
            return x >> ar | x << (1 +% ~ar);
        } else {
            const ar = @mod(r, @typeInfo(T).int.bits);
            return shr(T, x, ar) | shl(T, x, @typeInfo(T).int.bits - ar);
        }
    }
}

test rotr {
    try testing.expect(rotr(u0, 0b0, @as(usize, 3)) == 0b0);
    try testing.expect(rotr(u5, 0b00001, @as(usize, 0)) == 0b00001);
    try testing.expect(rotr(u6, 0b000001, @as(usize, 7)) == 0b100000);
    try testing.expect(rotr(u8, 0b00000001, @as(usize, 0)) == 0b00000001);
    try testing.expect(rotr(u8, 0b00000001, @as(usize, 9)) == 0b10000000);
    try testing.expect(rotr(u8, 0b00000001, @as(usize, 8)) == 0b00000001);
    try testing.expect(rotr(u8, 0b00000001, @as(usize, 4)) == 0b00010000);
    try testing.expect(rotr(u8, 0b00000001, @as(isize, -1)) == 0b00000010);
    try testing.expect(rotr(u12, 0o7777, 1) == 0o7777);
    try testing.expect(rotr(@Vector(1, u32), @Vector(1, u32){1}, @as(usize, 1))[0] == @as(u32, 1) << 31);
    try testing.expect(rotr(@Vector(1, u32), @Vector(1, u32){1}, @as(isize, -1))[0] == @as(u32, 1) << 1);
}

/// Rotates left. Only unsigned values can be rotated.  Negative shift
/// values result in shift modulo the bit count.
pub fn rotl(comptime T: type, x: T, r: anytype) T {
    if (@typeInfo(T) == .vector) {
        const C = @typeInfo(T).vector.child;
        if (C == u0) return 0;

        if (@typeInfo(C).int.signedness == .signed) {
            @compileError("cannot rotate signed integers");
        }
        const ar: Log2Int(C) = @intCast(@mod(r, @typeInfo(C).int.bits));
        return (x << @splat(ar)) | (x >> @splat(1 +% ~ar));
    } else if (@typeInfo(T).int.signedness == .signed) {
        @compileError("cannot rotate signed integer");
    } else {
        if (T == u0) return 0;

        if (comptime isPowerOfTwo(@typeInfo(T).int.bits)) {
            const ar: Log2Int(T) = @intCast(@mod(r, @typeInfo(T).int.bits));
            return x << ar | x >> 1 +% ~ar;
        } else {
            const ar = @mod(r, @typeInfo(T).int.bits);
            return shl(T, x, ar) | shr(T, x, @typeInfo(T).int.bits - ar);
        }
    }
}

test rotl {
    try testing.expect(rotl(u0, 0b0, @as(usize, 3)) == 0b0);
    try testing.expect(rotl(u5, 0b00001, @as(usize, 0)) == 0b00001);
    try testing.expect(rotl(u6, 0b000001, @as(usize, 7)) == 0b000010);
    try testing.expect(rotl(u8, 0b00000001, @as(usize, 0)) == 0b00000001);
    try testing.expect(rotl(u8, 0b00000001, @as(usize, 9)) == 0b00000010);
    try testing.expect(rotl(u8, 0b00000001, @as(usize, 8)) == 0b00000001);
    try testing.expect(rotl(u8, 0b00000001, @as(usize, 4)) == 0b00010000);
    try testing.expect(rotl(u8, 0b00000001, @as(isize, -1)) == 0b10000000);
    try testing.expect(rotl(u12, 0o7777, 1) == 0o7777);
    try testing.expect(rotl(@Vector(1, u32), @Vector(1, u32){1 << 31}, @as(usize, 1))[0] == 1);
    try testing.expect(rotl(@Vector(1, u32), @Vector(1, u32){1 << 31}, @as(isize, -1))[0] == @as(u32, 1) << 30);
}

/// Returns an unsigned int type that can hold the number of bits in T - 1.
/// Suitable for 0-based bit indices of T.
pub fn Log2Int(comptime T: type) type {
    // comptime ceil log2
    if (T == comptime_int) return comptime_int;
    const bits: u16 = @typeInfo(T).int.bits;
    const log2_bits = 16 - @clz(bits - 1);
    return std.meta.Int(.unsigned, log2_bits);
}

/// Returns an unsigned int type that can hold the number of bits in T.
pub fn Log2IntCeil(comptime T: type) type {
    // comptime ceil log2
    if (T == comptime_int) return comptime_int;
    const bits: u16 = @typeInfo(T).int.bits;
    const log2_bits = 16 - @clz(bits);
    return std.meta.Int(.unsigned, log2_bits);
}

/// Returns the smallest integer type that can hold both from and to.
pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) type {
    assert(from <= to);
    if (from == 0 and to == 0) {
        return u0;
    }
    const signedness: std.builtin.Signedness = if (from < 0) .signed else .unsigned;
    const largest_positive_integer = @max(if (from < 0) (-from) - 1 else from, to); // two's complement
    const base = log2(largest_positive_integer);
    const upper = (1 << base) - 1;
    var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1;
    if (signedness == .signed) {
        magnitude_bits += 1;
    }
    return std.meta.Int(signedness, magnitude_bits);
}

test IntFittingRange {
    try testing.expect(IntFittingRange(0, 0) == u0);
    try testing.expect(IntFittingRange(0, 1) == u1);
    try testing.expect(IntFittingRange(0, 2) == u2);
    try testing.expect(IntFittingRange(0, 3) == u2);
    try testing.expect(IntFittingRange(0, 4) == u3);
    try testing.expect(IntFittingRange(0, 7) == u3);
    try testing.expect(IntFittingRange(0, 8) == u4);
    try testing.expect(IntFittingRange(0, 9) == u4);
    try testing.expect(IntFittingRange(0, 15) == u4);
    try testing.expect(IntFittingRange(0, 16) == u5);
    try testing.expect(IntFittingRange(0, 17) == u5);
    try testing.expect(IntFittingRange(0, 4095) == u12);
    try testing.expect(IntFittingRange(2000, 4095) == u12);
    try testing.expect(IntFittingRange(0, 4096) == u13);
    try testing.expect(IntFittingRange(2000, 4096) == u13);
    try testing.expect(IntFittingRange(0, 4097) == u13);
    try testing.expect(IntFittingRange(2000, 4097) == u13);
    try testing.expect(IntFittingRange(0, 123456789123456798123456789) == u87);
    try testing.expect(IntFittingRange(0, 123456789123456798123456789123456789123456798123456789) == u177);

    try testing.expect(IntFittingRange(-1, -1) == i1);
    try testing.expect(IntFittingRange(-1, 0) == i1);
    try testing.expect(IntFittingRange(-1, 1) == i2);
    try testing.expect(IntFittingRange(-2, -2) == i2);
    try testing.expect(IntFittingRange(-2, -1) == i2);
    try testing.expect(IntFittingRange(-2, 0) == i2);
    try testing.expect(IntFittingRange(-2, 1) == i2);
    try testing.expect(IntFittingRange(-2, 2) == i3);
    try testing.expect(IntFittingRange(-1, 2) == i3);
    try testing.expect(IntFittingRange(-1, 3) == i3);
    try testing.expect(IntFittingRange(-1, 4) == i4);
    try testing.expect(IntFittingRange(-1, 7) == i4);
    try testing.expect(IntFittingRange(-1, 8) == i5);
    try testing.expect(IntFittingRange(-1, 9) == i5);
    try testing.expect(IntFittingRange(-1, 15) == i5);
    try testing.expect(IntFittingRange(-1, 16) == i6);
    try testing.expect(IntFittingRange(-1, 17) == i6);
    try testing.expect(IntFittingRange(-1, 4095) == i13);
    try testing.expect(IntFittingRange(-4096, 4095) == i13);
    try testing.expect(IntFittingRange(-1, 4096) == i14);
    try testing.expect(IntFittingRange(-4097, 4095) == i14);
    try testing.expect(IntFittingRange(-1, 4097) == i14);
    try testing.expect(IntFittingRange(-1, 123456789123456798123456789) == i88);
    try testing.expect(IntFittingRange(-1, 123456789123456798123456789123456789123456798123456789) == i178);
}

test "overflow functions" {
    try testOverflow();
    try comptime testOverflow();
}

fn testOverflow() !void {
    try testing.expect((mul(i32, 3, 4) catch unreachable) == 12);
    try testing.expect((add(i32, 3, 4) catch unreachable) == 7);
    try testing.expect((sub(i32, 3, 4) catch unreachable) == -1);
    try testing.expect((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000);
}

/// Divide numerator by denominator, rounding toward zero. Returns an
/// error on overflow or when denominator is zero.
pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T {
    @setRuntimeSafety(false);
    if (denominator == 0) return error.DivisionByZero;
    if (@typeInfo(T) == .int and @typeInfo(T).int.signedness == .signed and numerator == minInt(T) and denominator == -1) return error.Overflow;
    return @divTrunc(numerator, denominator);
}

test divTrunc {
    try testDivTrunc();
    try comptime testDivTrunc();
}
fn testDivTrunc() !void {
    try testing.expect((divTrunc(i32, 5, 3) catch unreachable) == 1);
    try testing.expect((divTrunc(i32, -5, 3) catch unreachable) == -1);
    try testing.expectError(error.DivisionByZero, divTrunc(i8, -5, 0));
    try testing.expectError(error.Overflow, divTrunc(i8, -128, -1));

    try testing.expect((divTrunc(f32, 5.0, 3.0) catch unreachable) == 1.0);
    try testing.expect((divTrunc(f32, -5.0, 3.0) catch unreachable) == -1.0);
}

/// Divide numerator by denominator, rounding toward negative
/// infinity. Returns an error on overflow or when denominator is
/// zero.
pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T {
    @setRuntimeSafety(false);
    if (denominator == 0) return error.DivisionByZero;
    if (@typeInfo(T) == .int and @typeInfo(T).int.signedness == .signed and numerator == minInt(T) and denominator == -1) return error.Overflow;
    return @divFloor(numerator, denominator);
}

test divFloor {
    try testDivFloor();
    try comptime testDivFloor();
}
fn testDivFloor() !void {
    try testing.expect((divFloor(i32, 5, 3) catch unreachable) == 1);
    try testing.expect((divFloor(i32, -5, 3) catch unreachable) == -2);
    try testing.expectError(error.DivisionByZero, divFloor(i8, -5, 0));
    try testing.expectError(error.Overflow, divFloor(i8, -128, -1));

    try testing.expect((divFloor(f32, 5.0, 3.0) catch unreachable) == 1.0);
    try testing.expect((divFloor(f32, -5.0, 3.0) catch unreachable) == -2.0);
}

/// Divide numerator by denominator, rounding toward positive
/// infinity. Returns an error on overflow or when denominator is
/// zero.
pub fn divCeil(comptime T: type, numerator: T, denominator: T) !T {
    @setRuntimeSafety(false);
    if (denominator == 0) return error.DivisionByZero;
    const info = @typeInfo(T);
    switch (info) {
        .comptime_float, .float => return @ceil(numerator / denominator),
        .comptime_int, .int => {
            if (numerator < 0 and denominator < 0) {
                if (info == .int and numerator == minInt(T) and denominator == -1)
                    return error.Overflow;
                return @divFloor(numerator + 1, denominator) + 1;
            }
            if (numerator > 0 and denominator > 0)
                return @divFloor(numerator - 1, denominator) + 1;
            return @divTrunc(numerator, denominator);
        },
        else => @compileError("divCeil unsupported on " ++ @typeName(T)),
    }
}

test divCeil {
    try testDivCeil();
    try comptime testDivCeil();
}
fn testDivCeil() !void {
    try testing.expectEqual(@as(i32, 2), divCeil(i32, 5, 3) catch unreachable);
    try testing.expectEqual(@as(i32, -1), divCeil(i32, -5, 3) catch unreachable);
    try testing.expectEqual(@as(i32, -1), divCeil(i32, 5, -3) catch unreachable);
    try testing.expectEqual(@as(i32, 2), divCeil(i32, -5, -3) catch unreachable);
    try testing.expectEqual(@as(i32, 0), divCeil(i32, 0, 5) catch unreachable);
    try testing.expectEqual(@as(u32, 0), divCeil(u32, 0, 5) catch unreachable);
    try testing.expectError(error.DivisionByZero, divCeil(i8, -5, 0));
    try testing.expectError(error.Overflow, divCeil(i8, -128, -1));

    try testing.expectEqual(@as(f32, 0.0), divCeil(f32, 0.0, 5.0) catch unreachable);
    try testing.expectEqual(@as(f32, 2.0), divCeil(f32, 5.0, 3.0) catch unreachable);
    try testing.expectEqual(@as(f32, -1.0), divCeil(f32, -5.0, 3.0) catch unreachable);
    try testing.expectEqual(@as(f32, -1.0), divCeil(f32, 5.0, -3.0) catch unreachable);
    try testing.expectEqual(@as(f32, 2.0), divCeil(f32, -5.0, -3.0) catch unreachable);

    try testing.expectEqual(6, divCeil(comptime_int, 23, 4) catch unreachable);
    try testing.expectEqual(-5, divCeil(comptime_int, -23, 4) catch unreachable);
    try testing.expectEqual(-5, divCeil(comptime_int, 23, -4) catch unreachable);
    try testing.expectEqual(6, divCeil(comptime_int, -23, -4) catch unreachable);
    try testing.expectError(error.DivisionByZero, divCeil(comptime_int, 23, 0));

    try testing.expectEqual(6.0, divCeil(comptime_float, 23.0, 4.0) catch unreachable);
    try testing.expectEqual(-5.0, divCeil(comptime_float, -23.0, 4.0) catch unreachable);
    try testing.expectEqual(-5.0, divCeil(comptime_float, 23.0, -4.0) catch unreachable);
    try testing.expectEqual(6.0, divCeil(comptime_float, -23.0, -4.0) catch unreachable);
    try testing.expectError(error.DivisionByZero, divCeil(comptime_float, 23.0, 0.0));
}

/// Divide numerator by denominator. Return an error if quotient is
/// not an integer, denominator is zero, or on overflow.
pub fn divExact(comptime T: type, numerator: T, denominator: T) !T {
    @setRuntimeSafety(false);
    if (denominator == 0) return error.DivisionByZero;
    if (@typeInfo(T) == .int and @typeInfo(T).int.signedness == .signed and numerator == minInt(T) and denominator == -1) return error.Overflow;
    const result = @divTrunc(numerator, denominator);
    if (result * denominator != numerator) return error.UnexpectedRemainder;
    return result;
}

test divExact {
    try testDivExact();
    try comptime testDivExact();
}
fn testDivExact() !void {
    try testing.expect((divExact(i32, 10, 5) catch unreachable) == 2);
    try testing.expect((divExact(i32, -10, 5) catch unreachable) == -2);
    try testing.expectError(error.DivisionByZero, divExact(i8, -5, 0));
    try testing.expectError(error.Overflow, divExact(i8, -128, -1));
    try testing.expectError(error.UnexpectedRemainder, divExact(i32, 5, 2));

    try testing.expect((divExact(f32, 10.0, 5.0) catch unreachable) == 2.0);
    try testing.expect((divExact(f32, -10.0, 5.0) catch unreachable) == -2.0);
    try testing.expectError(error.UnexpectedRemainder, divExact(f32, 5.0, 2.0));
}

/// Returns numerator modulo denominator, or an error if denominator is
/// zero or negative. Negative numerators never result in negative
/// return values.
pub fn mod(comptime T: type, numerator: T, denominator: T) !T {
    @setRuntimeSafety(false);
    if (denominator == 0) return error.DivisionByZero;
    if (denominator < 0) return error.NegativeDenominator;
    return @mod(numerator, denominator);
}

test mod {
    try testMod();
    try comptime testMod();
}
fn testMod() !void {
    try testing.expect((mod(i32, -5, 3) catch unreachable) == 1);
    try testing.expect((mod(i32, 5, 3) catch unreachable) == 2);
    try testing.expectError(error.NegativeDenominator, mod(i32, 10, -1));
    try testing.expectError(error.DivisionByZero, mod(i32, 10, 0));

    try testing.expect((mod(f32, -5, 3) catch unreachable) == 1);
    try testing.expect((mod(f32, 5, 3) catch unreachable) == 2);
    try testing.expectError(error.NegativeDenominator, mod(f32, 10, -1));
    try testing.expectError(error.DivisionByZero, mod(f32, 10, 0));
}

/// Returns the remainder when numerator is divided by denominator, or
/// an error if denominator is zero or negative. Negative numerators
/// can give negative results.
pub fn rem(comptime T: type, numerator: T, denominator: T) !T {
    @setRuntimeSafety(false);
    if (denominator == 0) return error.DivisionByZero;
    if (denominator < 0) return error.NegativeDenominator;
    return @rem(numerator, denominator);
}

test rem {
    try testRem();
    try comptime testRem();
}
fn testRem() !void {
    try testing.expect((rem(i32, -5, 3) catch unreachable) == -2);
    try testing.expect((rem(i32, 5, 3) catch unreachable) == 2);
    try testing.expectError(error.NegativeDenominator, rem(i32, 10, -1));
    try testing.expectError(error.DivisionByZero, rem(i32, 10, 0));

    try testing.expect((rem(f32, -5, 3) catch unreachable) == -2);
    try testing.expect((rem(f32, 5, 3) catch unreachable) == 2);
    try testing.expectError(error.NegativeDenominator, rem(f32, 10, -1));
    try testing.expectError(error.DivisionByZero, rem(f32, 10, 0));
}

/// Returns the negation of the integer parameter.
/// Result is a signed integer.
pub fn negateCast(x: anytype) !std.meta.Int(.signed, @bitSizeOf(@TypeOf(x))) {
    if (@typeInfo(@TypeOf(x)).int.signedness == .signed) return negate(x);

    const int = std.meta.Int(.signed, @bitSizeOf(@TypeOf(x)));
    if (x > -minInt(int)) return error.Overflow;

    if (x == -minInt(int)) return minInt(int);

    return -@as(int, @intCast(x));
}

test negateCast {
    try testing.expect((negateCast(@as(u32, 999)) catch unreachable) == -999);
    try testing.expect(@TypeOf(negateCast(@as(u32, 999)) catch unreachable) == i32);

    try testing.expect((negateCast(@as(u32, -minInt(i32))) catch unreachable) == minInt(i32));
    try testing.expect(@TypeOf(negateCast(@as(u32, -minInt(i32))) catch unreachable) == i32);

    try testing.expectError(error.Overflow, negateCast(@as(u32, maxInt(i32) + 10)));
}

/// Cast an integer to a different integer type. If the value doesn't fit,
/// return null.
pub fn cast(comptime T: type, x: anytype) ?T {
    comptime assert(@typeInfo(T) == .int); // must pass an integer
    const is_comptime = @TypeOf(x) == comptime_int;
    comptime assert(is_comptime or @typeInfo(@TypeOf(x)) == .int); // must pass an integer
    if ((is_comptime or maxInt(@TypeOf(x)) > maxInt(T)) and x > maxInt(T)) {
        return null;
    } else if ((is_comptime or minInt(@TypeOf(x)) < minInt(T)) and x < minInt(T)) {
        return null;
    } else {
        return @as(T, @intCast(x));
    }
}

test cast {
    try testing.expect(cast(u8, 300) == null);
    try testing.expect(cast(u8, @as(u32, 300)) == null);
    try testing.expect(cast(i8, -200) == null);
    try testing.expect(cast(i8, @as(i32, -200)) == null);
    try testing.expect(cast(u8, -1) == null);
    try testing.expect(cast(u8, @as(i8, -1)) == null);
    try testing.expect(cast(u64, -1) == null);
    try testing.expect(cast(u64, @as(i8, -1)) == null);

    try testing.expect(cast(u8, 255).? == @as(u8, 255));
    try testing.expect(cast(u8, @as(u32, 255)).? == @as(u8, 255));
    try testing.expect(@TypeOf(cast(u8, 255).?) == u8);
    try testing.expect(@TypeOf(cast(u8, @as(u32, 255)).?) == u8);
}

pub const AlignCastError = error{UnalignedMemory};

fn AlignCastResult(comptime alignment: u29, comptime Ptr: type) type {
    var ptr_info = @typeInfo(Ptr);
    ptr_info.pointer.alignment = alignment;
    return @Type(ptr_info);
}

/// Align cast a pointer but return an error if it's the wrong alignment
pub fn alignCast(comptime alignment: u29, ptr: anytype) AlignCastError!AlignCastResult(alignment, @TypeOf(ptr)) {
    const addr = @intFromPtr(ptr);
    if (addr % alignment != 0) {
        return error.UnalignedMemory;
    }
    return @alignCast(ptr);
}

/// Asserts `int > 0`.
pub fn isPowerOfTwo(int: anytype) bool {
    assert(int > 0);
    return (int & (int - 1)) == 0;
}

test isPowerOfTwo {
    try testing.expect(isPowerOfTwo(@as(u8, 1)));
    try testing.expect(isPowerOfTwo(2));
    try testing.expect(!isPowerOfTwo(@as(i16, 3)));
    try testing.expect(isPowerOfTwo(4));
    try testing.expect(!isPowerOfTwo(@as(u32, 31)));
    try testing.expect(isPowerOfTwo(32));
    try testing.expect(!isPowerOfTwo(@as(i64, 63)));
    try testing.expect(isPowerOfTwo(128));
    try testing.expect(isPowerOfTwo(@as(u128, 256)));
}

/// Aligns the given integer type bit width to a width divisible by 8.
pub fn ByteAlignedInt(comptime T: type) type {
    const info = @typeInfo(T).int;
    const bits = (info.bits + 7) / 8 * 8;
    const extended_type = std.meta.Int(info.signedness, bits);
    return extended_type;
}

test ByteAlignedInt {
    try testing.expect(ByteAlignedInt(u0) == u0);
    try testing.expect(ByteAlignedInt(i0) == i0);
    try testing.expect(ByteAlignedInt(u3) == u8);
    try testing.expect(ByteAlignedInt(u8) == u8);
    try testing.expect(ByteAlignedInt(i111) == i112);
    try testing.expect(ByteAlignedInt(u129) == u136);
}

/// Rounds the given floating point number to the nearest integer.
/// If two integers are equally close, rounds away from zero.
/// Uses a dedicated hardware instruction when available.
/// This is the same as calling the builtin @round
pub inline fn round(value: anytype) @TypeOf(value) {
    return @round(value);
}

/// Rounds the given floating point number to an integer, towards zero.
/// Uses a dedicated hardware instruction when available.
/// This is the same as calling the builtin @trunc
pub inline fn trunc(value: anytype) @TypeOf(value) {
    return @trunc(value);
}

/// Returns the largest integral value not greater than the given floating point number.
/// Uses a dedicated hardware instruction when available.
/// This is the same as calling the builtin @floor
pub inline fn floor(value: anytype) @TypeOf(value) {
    return @floor(value);
}

/// Returns the nearest power of two less than or equal to value, or
/// zero if value is less than or equal to zero.
pub fn floorPowerOfTwo(comptime T: type, value: T) T {
    const uT = std.meta.Int(.unsigned, @typeInfo(T).int.bits);
    if (value <= 0) return 0;
    return @as(T, 1) << log2_int(uT, @as(uT, @intCast(value)));
}

test floorPowerOfTwo {
    try testFloorPowerOfTwo();
    try comptime testFloorPowerOfTwo();
}

fn testFloorPowerOfTwo() !void {
    try testing.expect(floorPowerOfTwo(u32, 63) == 32);
    try testing.expect(floorPowerOfTwo(u32, 64) == 64);
    try testing.expect(floorPowerOfTwo(u32, 65) == 64);
    try testing.expect(floorPowerOfTwo(u32, 0) == 0);
    try testing.expect(floorPowerOfTwo(u4, 7) == 4);
    try testing.expect(floorPowerOfTwo(u4, 8) == 8);
    try testing.expect(floorPowerOfTwo(u4, 9) == 8);
    try testing.expect(floorPowerOfTwo(u4, 0) == 0);
    try testing.expect(floorPowerOfTwo(i4, 7) == 4);
    try testing.expect(floorPowerOfTwo(i4, -8) == 0);
    try testing.expect(floorPowerOfTwo(i4, -1) == 0);
    try testing.expect(floorPowerOfTwo(i4, 0) == 0);
}

/// Returns the smallest integral value not less than the given floating point number.
/// Uses a dedicated hardware instruction when available.
/// This is the same as calling the builtin @ceil
pub inline fn ceil(value: anytype) @TypeOf(value) {
    return @ceil(value);
}

/// Returns the next power of two (if the value is not already a power of two).
/// Only unsigned integers can be used. Zero is not an allowed input.
/// Result is a type with 1 more bit than the input type.
pub fn ceilPowerOfTwoPromote(comptime T: type, value: T) std.meta.Int(@typeInfo(T).int.signedness, @typeInfo(T).int.bits + 1) {
    comptime assert(@typeInfo(T) == .int);
    comptime assert(@typeInfo(T).int.signedness == .unsigned);
    assert(value != 0);
    const PromotedType = std.meta.Int(@typeInfo(T).int.signedness, @typeInfo(T).int.bits + 1);
    const ShiftType = std.math.Log2Int(PromotedType);
    return @as(PromotedType, 1) << @as(ShiftType, @intCast(@typeInfo(T).int.bits - @clz(value - 1)));
}

/// Returns the next power of two (if the value is not already a power of two).
/// Only unsigned integers can be used. Zero is not an allowed input.
/// If the value doesn't fit, returns an error.
pub fn ceilPowerOfTwo(comptime T: type, value: T) (error{Overflow}!T) {
    comptime assert(@typeInfo(T) == .int);
    const info = @typeInfo(T).int;
    comptime assert(info.signedness == .unsigned);
    const PromotedType = std.meta.Int(info.signedness, info.bits + 1);
    const overflowBit = @as(PromotedType, 1) << info.bits;
    const x = ceilPowerOfTwoPromote(T, value);
    if (overflowBit & x != 0) {
        return error.Overflow;
    }
    return @as(T, @intCast(x));
}

/// Returns the next power of two (if the value is not already a power
/// of two). Only unsigned integers can be used. Zero is not an
/// allowed input. Asserts that the value fits.
pub fn ceilPowerOfTwoAssert(comptime T: type, value: T) T {
    return ceilPowerOfTwo(T, value) catch unreachable;
}

test ceilPowerOfTwoPromote {
    try testCeilPowerOfTwoPromote();
    try comptime testCeilPowerOfTwoPromote();
}

fn testCeilPowerOfTwoPromote() !void {
    try testing.expectEqual(@as(u33, 1), ceilPowerOfTwoPromote(u32, 1));
    try testing.expectEqual(@as(u33, 2), ceilPowerOfTwoPromote(u32, 2));
    try testing.expectEqual(@as(u33, 64), ceilPowerOfTwoPromote(u32, 63));
    try testing.expectEqual(@as(u33, 64), ceilPowerOfTwoPromote(u32, 64));
    try testing.expectEqual(@as(u33, 128), ceilPowerOfTwoPromote(u32, 65));
    try testing.expectEqual(@as(u6, 8), ceilPowerOfTwoPromote(u5, 7));
    try testing.expectEqual(@as(u6, 8), ceilPowerOfTwoPromote(u5, 8));
    try testing.expectEqual(@as(u6, 16), ceilPowerOfTwoPromote(u5, 9));
    try testing.expectEqual(@as(u5, 16), ceilPowerOfTwoPromote(u4, 9));
}

test ceilPowerOfTwo {
    try testCeilPowerOfTwo();
    try comptime testCeilPowerOfTwo();
}

fn testCeilPowerOfTwo() !void {
    try testing.expectEqual(@as(u32, 1), try ceilPowerOfTwo(u32, 1));
    try testing.expectEqual(@as(u32, 2), try ceilPowerOfTwo(u32, 2));
    try testing.expectEqual(@as(u32, 64), try ceilPowerOfTwo(u32, 63));
    try testing.expectEqual(@as(u32, 64), try ceilPowerOfTwo(u32, 64));
    try testing.expectEqual(@as(u32, 128), try ceilPowerOfTwo(u32, 65));
    try testing.expectEqual(@as(u5, 8), try ceilPowerOfTwo(u5, 7));
    try testing.expectEqual(@as(u5, 8), try ceilPowerOfTwo(u5, 8));
    try testing.expectEqual(@as(u5, 16), try ceilPowerOfTwo(u5, 9));
    try testing.expectError(error.Overflow, ceilPowerOfTwo(u4, 9));
}

/// Return the log base 2 of integer value x, rounding down to the
/// nearest integer.
pub fn log2_int(comptime T: type, x: T) Log2Int(T) {
    if (@typeInfo(T) != .int or @typeInfo(T).int.signedness != .unsigned)
        @compileError("log2_int requires an unsigned integer, found " ++ @typeName(T));
    assert(x != 0);
    return @as(Log2Int(T), @intCast(@typeInfo(T).int.bits - 1 - @clz(x)));
}

/// Return the log base 2 of integer value x, rounding up to the
/// nearest integer.
pub fn log2_int_ceil(comptime T: type, x: T) Log2IntCeil(T) {
    if (@typeInfo(T) != .int or @typeInfo(T).int.signedness != .unsigned)
        @compileError("log2_int_ceil requires an unsigned integer, found " ++ @typeName(T));
    assert(x != 0);
    if (x == 1) return 0;
    const log2_val: Log2IntCeil(T) = log2_int(T, x - 1);
    return log2_val + 1;
}

test log2_int_ceil {
    try testing.expect(log2_int_ceil(u32, 1) == 0);
    try testing.expect(log2_int_ceil(u32, 2) == 1);
    try testing.expect(log2_int_ceil(u32, 3) == 2);
    try testing.expect(log2_int_ceil(u32, 4) == 2);
    try testing.expect(log2_int_ceil(u32, 5) == 3);
    try testing.expect(log2_int_ceil(u32, 6) == 3);
    try testing.expect(log2_int_ceil(u32, 7) == 3);
    try testing.expect(log2_int_ceil(u32, 8) == 3);
    try testing.expect(log2_int_ceil(u32, 9) == 4);
    try testing.expect(log2_int_ceil(u32, 10) == 4);
}

/// Cast a value to a different type. If the value doesn't fit in, or
/// can't be perfectly represented by, the new type, it will be
/// converted to the closest possible representation.
pub fn lossyCast(comptime T: type, value: anytype) T {
    switch (@typeInfo(T)) {
        .float => {
            switch (@typeInfo(@TypeOf(value))) {
                .int => return @floatFromInt(value),
                .float => return @floatCast(value),
                .comptime_int => return value,
                .comptime_float => return value,
                else => @compileError("bad type"),
            }
        },
        .int => {
            switch (@typeInfo(@TypeOf(value))) {
                .int, .comptime_int => {
                    if (value >= maxInt(T)) {
                        return maxInt(T);
                    } else if (value <= minInt(T)) {
                        return minInt(T);
                    } else {
                        return @intCast(value);
                    }
                },
                .float, .comptime_float => {
                    if (isNan(value)) {
                        return 0;
                    } else if (value >= maxInt(T)) {
                        return maxInt(T);
                    } else if (value <= minInt(T)) {
                        return minInt(T);
                    } else {
                        return @intFromFloat(value);
                    }
                },
                else => @compileError("bad type"),
            }
        },
        else => @compileError("bad result type"),
    }
}

test lossyCast {
    try testing.expect(lossyCast(i16, 70000.0) == @as(i16, 32767));
    try testing.expect(lossyCast(u32, @as(i16, -255)) == @as(u32, 0));
    try testing.expect(lossyCast(i9, @as(u32, 200)) == @as(i9, 200));
    try testing.expect(lossyCast(u32, @as(f32, maxInt(u32))) == maxInt(u32));
    try testing.expect(lossyCast(u32, nan(f32)) == 0);
}

/// Performs linear interpolation between *a* and *b* based on *t*.
/// *t* ranges from 0.0 to 1.0, but may exceed these bounds.
/// Supports floats and vectors of floats.
///
/// This does not guarantee returning *b* if *t* is 1 due to floating-point errors.
/// This is monotonic.
pub fn lerp(a: anytype, b: anytype, t: anytype) @TypeOf(a, b, t) {
    const Type = @TypeOf(a, b, t);
    return @mulAdd(Type, b - a, t, a);
}

test lerp {
    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/17884
    if (builtin.zig_backend == .stage2_x86_64 and
        !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .fma)) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/17884

    try testing.expectEqual(@as(f64, 75), lerp(50, 100, 0.5));
    try testing.expectEqual(@as(f32, 43.75), lerp(50, 25, 0.25));
    try testing.expectEqual(@as(f64, -31.25), lerp(-50, 25, 0.25));

    try testing.expectEqual(@as(f64, 30), lerp(10, 20, 2.0));
    try testing.expectEqual(@as(f64, 5), lerp(10, 20, -0.5));

    try testing.expectApproxEqRel(@as(f32, -7.16067345e+03), lerp(-10000.12345, -5000.12345, 0.56789), 1e-19);
    try testing.expectApproxEqRel(@as(f64, 7.010987590521e+62), lerp(0.123456789e-64, 0.123456789e64, 0.56789), 1e-33);

    try testing.expectEqual(@as(f32, 0.0), lerp(@as(f32, 1.0e8), 1.0, 1.0));
    try testing.expectEqual(@as(f64, 0.0), lerp(@as(f64, 1.0e16), 1.0, 1.0));
    try testing.expectEqual(@as(f32, 1.0), lerp(@as(f32, 1.0e7), 1.0, 1.0));
    try testing.expectEqual(@as(f64, 1.0), lerp(@as(f64, 1.0e15), 1.0, 1.0));

    {
        const a: @Vector(3, f32) = @splat(0);
        const b: @Vector(3, f32) = @splat(50);
        const t: @Vector(3, f32) = @splat(0.5);
        try testing.expectEqual(
            @Vector(3, f32){ 25, 25, 25 },
            lerp(a, b, t),
        );
    }
    {
        const a: @Vector(3, f64) = @splat(50);
        const b: @Vector(3, f64) = @splat(100);
        const t: @Vector(3, f64) = @splat(0.5);
        try testing.expectEqual(
            @Vector(3, f64){ 75, 75, 75 },
            lerp(a, b, t),
        );
    }
    {
        const a: @Vector(2, f32) = @splat(40);
        const b: @Vector(2, f32) = @splat(80);
        const t: @Vector(2, f32) = @Vector(2, f32){ 0.25, 0.75 };
        try testing.expectEqual(
            @Vector(2, f32){ 50, 70 },
            lerp(a, b, t),
        );
    }
}

/// Returns the maximum value of integer type T.
pub fn maxInt(comptime T: type) comptime_int {
    const info = @typeInfo(T);
    const bit_count = info.int.bits;
    if (bit_count == 0) return 0;
    return (1 << (bit_count - @intFromBool(info.int.signedness == .signed))) - 1;
}

/// Returns the minimum value of integer type T.
pub fn minInt(comptime T: type) comptime_int {
    const info = @typeInfo(T);
    const bit_count = info.int.bits;
    if (info.int.signedness == .unsigned) return 0;
    if (bit_count == 0) return 0;
    return -(1 << (bit_count - 1));
}

test maxInt {
    try testing.expect(maxInt(u0) == 0);
    try testing.expect(maxInt(u1) == 1);
    try testing.expect(maxInt(u8) == 255);
    try testing.expect(maxInt(u16) == 65535);
    try testing.expect(maxInt(u32) == 4294967295);
    try testing.expect(maxInt(u64) == 18446744073709551615);
    try testing.expect(maxInt(u128) == 340282366920938463463374607431768211455);

    try testing.expect(maxInt(i0) == 0);
    try testing.expect(maxInt(i1) == 0);
    try testing.expect(maxInt(i8) == 127);
    try testing.expect(maxInt(i16) == 32767);
    try testing.expect(maxInt(i32) == 2147483647);
    try testing.expect(maxInt(i63) == 4611686018427387903);
    try testing.expect(maxInt(i64) == 9223372036854775807);
    try testing.expect(maxInt(i128) == 170141183460469231731687303715884105727);
}

test minInt {
    try testing.expect(minInt(u0) == 0);
    try testing.expect(minInt(u1) == 0);
    try testing.expect(minInt(u8) == 0);
    try testing.expect(minInt(u16) == 0);
    try testing.expect(minInt(u32) == 0);
    try testing.expect(minInt(u63) == 0);
    try testing.expect(minInt(u64) == 0);
    try testing.expect(minInt(u128) == 0);

    try testing.expect(minInt(i0) == 0);
    try testing.expect(minInt(i1) == -1);
    try testing.expect(minInt(i8) == -128);
    try testing.expect(minInt(i16) == -32768);
    try testing.expect(minInt(i32) == -2147483648);
    try testing.expect(minInt(i63) == -4611686018427387904);
    try testing.expect(minInt(i64) == -9223372036854775808);
    try testing.expect(minInt(i128) == -170141183460469231731687303715884105728);
}

test "max value type" {
    const x: u32 = maxInt(i32);
    try testing.expect(x == 2147483647);
}

/// Multiply a and b. Return type is wide enough to guarantee no
/// overflow.
pub fn mulWide(comptime T: type, a: T, b: T) std.meta.Int(
    @typeInfo(T).int.signedness,
    @typeInfo(T).int.bits * 2,
) {
    const ResultInt = std.meta.Int(
        @typeInfo(T).int.signedness,
        @typeInfo(T).int.bits * 2,
    );
    return @as(ResultInt, a) * @as(ResultInt, b);
}

test mulWide {
    try testing.expect(mulWide(u8, 5, 5) == 25);
    try testing.expect(mulWide(i8, 5, -5) == -25);
    try testing.expect(mulWide(u8, 100, 100) == 10000);
}

/// See also `CompareOperator`.
pub const Order = enum {
    /// Greater than (`>`)
    gt,

    /// Less than (`<`)
    lt,

    /// Equal (`==`)
    eq,

    pub fn invert(self: Order) Order {
        return switch (self) {
            .lt => .gt,
            .eq => .eq,
            .gt => .lt,
        };
    }

    test invert {
        try testing.expect(Order.invert(order(0, 0)) == .eq);
        try testing.expect(Order.invert(order(1, 0)) == .lt);
        try testing.expect(Order.invert(order(-1, 0)) == .gt);
    }

    pub fn differ(self: Order) ?Order {
        return if (self != .eq) self else null;
    }

    test differ {
        const neg: i32 = -1;
        const zero: i32 = 0;
        const pos: i32 = 1;
        try testing.expect(order(zero, neg).differ() orelse
            order(pos, zero) == .gt);
        try testing.expect(order(zero, zero).differ() orelse
            order(zero, zero) == .eq);
        try testing.expect(order(pos, pos).differ() orelse
            order(neg, zero) == .lt);
        try testing.expect(order(zero, zero).differ() orelse
            order(pos, neg).differ() orelse
            order(neg, zero) == .gt);
        try testing.expect(order(pos, pos).differ() orelse
            order(pos, pos).differ() orelse
            order(neg, neg) == .eq);
        try testing.expect(order(zero, pos).differ() orelse
            order(neg, pos).differ() orelse
            order(pos, neg) == .lt);
    }

    pub fn compare(self: Order, op: CompareOperator) bool {
        return switch (self) {
            .lt => switch (op) {
                .lt => true,
                .lte => true,
                .eq => false,
                .gte => false,
                .gt => false,
                .neq => true,
            },
            .eq => switch (op) {
                .lt => false,
                .lte => true,
                .eq => true,
                .gte => true,
                .gt => false,
                .neq => false,
            },
            .gt => switch (op) {
                .lt => false,
                .lte => false,
                .eq => false,
                .gte => true,
                .gt => true,
                .neq => true,
            },
        };
    }

    // https://github.com/ziglang/zig/issues/19295
    test "compare" {
        try testing.expect(order(-1, 0).compare(.lt));
        try testing.expect(order(-1, 0).compare(.lte));
        try testing.expect(order(0, 0).compare(.lte));
        try testing.expect(order(0, 0).compare(.eq));
        try testing.expect(order(0, 0).compare(.gte));
        try testing.expect(order(1, 0).compare(.gte));
        try testing.expect(order(1, 0).compare(.gt));
        try testing.expect(order(1, 0).compare(.neq));
    }
};

/// Given two numbers, this function returns the order they are with respect to each other.
pub fn order(a: anytype, b: anytype) Order {
    if (a == b) {
        return .eq;
    } else if (a < b) {
        return .lt;
    } else if (a > b) {
        return .gt;
    } else {
        unreachable;
    }
}

/// See also `Order`.
pub const CompareOperator = enum {
    /// Less than (`<`)
    lt,
    /// Less than or equal (`<=`)
    lte,
    /// Equal (`==`)
    eq,
    /// Greater than or equal (`>=`)
    gte,
    /// Greater than (`>`)
    gt,
    /// Not equal (`!=`)
    neq,

    /// Reverse the direction of the comparison.
    /// Use when swapping the left and right hand operands.
    pub fn reverse(op: CompareOperator) CompareOperator {
        return switch (op) {
            .lt => .gt,
            .lte => .gte,
            .gt => .lt,
            .gte => .lte,
            .eq => .eq,
            .neq => .neq,
        };
    }

    test reverse {
        inline for (@typeInfo(CompareOperator).@"enum".fields) |op_field| {
            const op = @as(CompareOperator, @enumFromInt(op_field.value));
            try testing.expect(compare(2, op, 3) == compare(3, op.reverse(), 2));
            try testing.expect(compare(3, op, 3) == compare(3, op.reverse(), 3));
            try testing.expect(compare(4, op, 3) == compare(3, op.reverse(), 4));
        }
    }
};

/// This function does the same thing as comparison operators, however the
/// operator is a runtime-known enum value. Works on any operands that
/// support comparison operators.
pub fn compare(a: anytype, op: CompareOperator, b: anytype) bool {
    return switch (op) {
        .lt => a < b,
        .lte => a <= b,
        .eq => a == b,
        .neq => a != b,
        .gt => a > b,
        .gte => a >= b,
    };
}

test compare {
    try testing.expect(compare(@as(i8, -1), .lt, @as(u8, 255)));
    try testing.expect(compare(@as(i8, 2), .gt, @as(u8, 1)));
    try testing.expect(!compare(@as(i8, -1), .gte, @as(u8, 255)));
    try testing.expect(compare(@as(u8, 255), .gt, @as(i8, -1)));
    try testing.expect(!compare(@as(u8, 255), .lte, @as(i8, -1)));
    try testing.expect(compare(@as(i8, -1), .lt, @as(u9, 255)));
    try testing.expect(!compare(@as(i8, -1), .gte, @as(u9, 255)));
    try testing.expect(compare(@as(u9, 255), .gt, @as(i8, -1)));
    try testing.expect(!compare(@as(u9, 255), .lte, @as(i8, -1)));
    try testing.expect(compare(@as(i9, -1), .lt, @as(u8, 255)));
    try testing.expect(!compare(@as(i9, -1), .gte, @as(u8, 255)));
    try testing.expect(compare(@as(u8, 255), .gt, @as(i9, -1)));
    try testing.expect(!compare(@as(u8, 255), .lte, @as(i9, -1)));
    try testing.expect(compare(@as(u8, 1), .lt, @as(u8, 2)));
    try testing.expect(@as(u8, @bitCast(@as(i8, -1))) == @as(u8, 255));
    try testing.expect(!compare(@as(u8, 255), .eq, @as(i8, -1)));
    try testing.expect(compare(@as(u8, 1), .eq, @as(u8, 1)));
}

test order {
    try testing.expect(order(0, 0) == .eq);
    try testing.expect(order(1, 0) == .gt);
    try testing.expect(order(-1, 0) == .lt);
}

/// Returns a mask of all ones if value is true,
/// and a mask of all zeroes if value is false.
/// Compiles to one instruction for register sized integers.
pub inline fn boolMask(comptime MaskInt: type, value: bool) MaskInt {
    if (@typeInfo(MaskInt) != .int)
        @compileError("boolMask requires an integer mask type.");

    if (MaskInt == u0 or MaskInt == i0)
        @compileError("boolMask cannot convert to u0 or i0, they are too small.");

    // The u1 and i1 cases tend to overflow,
    // so we special case them here.
    if (MaskInt == u1) return @intFromBool(value);
    if (MaskInt == i1) {
        // The @as here is a workaround for #7950
        return @as(i1, @bitCast(@as(u1, @intFromBool(value))));
    }

    return -%@as(MaskInt, @intCast(@intFromBool(value)));
}

test boolMask {
    const runTest = struct {
        fn runTest() !void {
            try testing.expectEqual(@as(u1, 0), boolMask(u1, false));
            try testing.expectEqual(@as(u1, 1), boolMask(u1, true));

            try testing.expectEqual(@as(i1, 0), boolMask(i1, false));
            try testing.expectEqual(@as(i1, -1), boolMask(i1, true));

            try testing.expectEqual(@as(u13, 0), boolMask(u13, false));
            try testing.expectEqual(@as(u13, 0x1FFF), boolMask(u13, true));

            try testing.expectEqual(@as(i13, 0), boolMask(i13, false));
            try testing.expectEqual(@as(i13, -1), boolMask(i13, true));

            try testing.expectEqual(@as(u32, 0), boolMask(u32, false));
            try testing.expectEqual(@as(u32, 0xFFFF_FFFF), boolMask(u32, true));

            try testing.expectEqual(@as(i32, 0), boolMask(i32, false));
            try testing.expectEqual(@as(i32, -1), boolMask(i32, true));
        }
    }.runTest;
    try runTest();
    try comptime runTest();
}

/// Return the mod of `num` with the smallest integer type
pub fn comptimeMod(num: anytype, comptime denom: comptime_int) IntFittingRange(0, denom - 1) {
    return @as(IntFittingRange(0, denom - 1), @intCast(@mod(num, denom)));
}

pub const F80 = struct {
    fraction: u64,
    exp: u16,

    pub fn toFloat(self: F80) f80 {
        const int = (@as(u80, self.exp) << 64) | self.fraction;
        return @as(f80, @bitCast(int));
    }

    pub fn fromFloat(x: f80) F80 {
        const int = @as(u80, @bitCast(x));
        return .{
            .fraction = @as(u64, @truncate(int)),
            .exp = @as(u16, @truncate(int >> 64)),
        };
    }
};

/// Returns -1, 0, or 1.
/// Supports integer and float types and vectors of integer and float types.
/// Unsigned integer types will always return 0 or 1.
/// Branchless.
pub inline fn sign(i: anytype) @TypeOf(i) {
    const T = @TypeOf(i);
    return switch (@typeInfo(T)) {
        .int, .comptime_int => @as(T, @intFromBool(i > 0)) - @as(T, @intFromBool(i < 0)),
        .float, .comptime_float => @as(T, @floatFromInt(@intFromBool(i > 0))) - @as(T, @floatFromInt(@intFromBool(i < 0))),
        .vector => |vinfo| blk: {
            switch (@typeInfo(vinfo.child)) {
                .int, .float => {
                    const zero: T = @splat(0);
                    const one: T = @splat(1);
                    break :blk @select(vinfo.child, i > zero, one, zero) - @select(vinfo.child, i < zero, one, zero);
                },
                else => @compileError("Expected vector of ints or floats, found " ++ @typeName(T)),
            }
        },
        else => @compileError("Expected an int, float or vector of one, found " ++ @typeName(T)),
    };
}

fn testSign() !void {
    // each of the following blocks checks the inputs
    // 2, -2, 0, { 2, -2, 0 } provide expected output
    // 1, -1, 0, { 1, -1, 0 } for the given T
    // (negative values omitted for unsigned types)
    {
        const T = i8;
        try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2)));
        try std.testing.expectEqual(@as(T, -1), sign(@as(T, -2)));
        try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0)));
        try std.testing.expectEqual(@Vector(3, T){ 1, -1, 0 }, sign(@Vector(3, T){ 2, -2, 0 }));
    }
    {
        const T = i32;
        try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2)));
        try std.testing.expectEqual(@as(T, -1), sign(@as(T, -2)));
        try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0)));
        try std.testing.expectEqual(@Vector(3, T){ 1, -1, 0 }, sign(@Vector(3, T){ 2, -2, 0 }));
    }
    {
        const T = i64;
        try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2)));
        try std.testing.expectEqual(@as(T, -1), sign(@as(T, -2)));
        try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0)));
        try std.testing.expectEqual(@Vector(3, T){ 1, -1, 0 }, sign(@Vector(3, T){ 2, -2, 0 }));
    }
    {
        const T = u8;
        try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2)));
        try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0)));
        try std.testing.expectEqual(@Vector(2, T){ 1, 0 }, sign(@Vector(2, T){ 2, 0 }));
    }
    {
        const T = u32;
        try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2)));
        try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0)));
        try std.testing.expectEqual(@Vector(2, T){ 1, 0 }, sign(@Vector(2, T){ 2, 0 }));
    }
    {
        const T = u64;
        try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2)));
        try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0)));
        try std.testing.expectEqual(@Vector(2, T){ 1, 0 }, sign(@Vector(2, T){ 2, 0 }));
    }
    {
        const T = f16;
        try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2)));
        try std.testing.expectEqual(@as(T, -1), sign(@as(T, -2)));
        try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0)));
        try std.testing.expectEqual(@Vector(3, T){ 1, -1, 0 }, sign(@Vector(3, T){ 2, -2, 0 }));
    }
    {
        const T = f32;
        try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2)));
        try std.testing.expectEqual(@as(T, -1), sign(@as(T, -2)));
        try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0)));
        try std.testing.expectEqual(@Vector(3, T){ 1, -1, 0 }, sign(@Vector(3, T){ 2, -2, 0 }));
    }
    {
        const T = f64;
        try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2)));
        try std.testing.expectEqual(@as(T, -1), sign(@as(T, -2)));
        try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0)));
        try std.testing.expectEqual(@Vector(3, T){ 1, -1, 0 }, sign(@Vector(3, T){ 2, -2, 0 }));
    }

    // comptime_int
    try std.testing.expectEqual(-1, sign(-10));
    try std.testing.expectEqual(1, sign(10));
    try std.testing.expectEqual(0, sign(0));
    // comptime_float
    try std.testing.expectEqual(-1.0, sign(-10.0));
    try std.testing.expectEqual(1.0, sign(10.0));
    try std.testing.expectEqual(0.0, sign(0.0));
}

test sign {
    try testSign();
    try comptime testSign();
}