mirror of
https://github.com/ziglang/zig.git
synced 2024-11-29 16:42:31 +00:00
4c16f9a3c3
This covers the majority of the functions as covered by the C99 specification for a math library. Code is adapted primarily from musl libc, with the pow and standard trigonometric functions adapted from the Go stdlib. Changes: - Remove assert expose in index and import as needed. - Add float log function and merge with existing base 2 integer implementation. See https://github.com/tiehuis/zig-fmath. See #374.
191 lines
4.2 KiB
Zig
191 lines
4.2 KiB
Zig
const math = @import("index.zig");
|
|
const assert = @import("../debug.zig").assert;
|
|
|
|
pub fn fmod(comptime T: type, x: T, y: T) -> T {
|
|
switch (T) {
|
|
f32 => @inlineCall(fmod32, x, y),
|
|
f64 => @inlineCall(fmod64, x, y),
|
|
else => @compileError("fmod not implemented for " ++ @typeName(T)),
|
|
}
|
|
}
|
|
|
|
fn fmod32(x: f32, y: f32) -> f32 {
|
|
var ux = @bitCast(u32, x);
|
|
var uy = @bitCast(u32, y);
|
|
var ex = i32(ux >> 23) & 0xFF;
|
|
var ey = i32(ux >> 23) & 0xFF;
|
|
const sx = ux & 0x80000000;
|
|
|
|
if (uy << 1 == 0 or math.isNan(y) or ex == 0xFF) {
|
|
return (x * y) / (x * y);
|
|
}
|
|
if (ux << 1 <= uy << 1) {
|
|
if (ux << 1 == uy << 1) {
|
|
return 0 * x;
|
|
} else {
|
|
return x;
|
|
}
|
|
}
|
|
|
|
// normalize x and y
|
|
if (ex == 0) {
|
|
var i = ux << 9;
|
|
while (i >> 31 == 0) : (i <<= 1) {
|
|
ex -= 1;
|
|
}
|
|
ux <<= u32(-ex + 1);
|
|
} else {
|
|
ux &= @maxValue(u32) >> 9;
|
|
ux |= 1 << 23;
|
|
}
|
|
|
|
if (ey == 0) {
|
|
var i = uy << 9;
|
|
while (i >> 31 == 0) : (i <<= 1) {
|
|
ey -= 1;
|
|
}
|
|
uy <<= u32(-ey + 1);
|
|
} else {
|
|
uy &= @maxValue(u32) >> 9;
|
|
uy |= 1 << 23;
|
|
}
|
|
|
|
// x mod y
|
|
while (ex > ey) : (ex -= 1) {
|
|
const i = ux - uy;
|
|
if (i >> 31 == 0) {
|
|
if (i == 0) {
|
|
return 0 * x;
|
|
}
|
|
ux = i;
|
|
}
|
|
ux <<= 1;
|
|
}
|
|
{
|
|
const i = ux - uy;
|
|
if (i >> 31 == 0) {
|
|
if (i == 0) {
|
|
return 0 * x;
|
|
}
|
|
ux = i;
|
|
}
|
|
}
|
|
|
|
while (ux >> 23 == 0) : (ux <<= 1) {
|
|
ex -= 1;
|
|
}
|
|
|
|
// scale result up
|
|
if (ex > 0) {
|
|
ux -= 1 << 23;
|
|
ux |= u32(ex) << 23;
|
|
} else {
|
|
ux >>= u32(-ex + 1);
|
|
}
|
|
|
|
ux |= sx;
|
|
@bitCast(f32, ux)
|
|
}
|
|
|
|
fn fmod64(x: f64, y: f64) -> f64 {
|
|
var ux = @bitCast(u64, x);
|
|
var uy = @bitCast(u64, y);
|
|
var ex = i32(ux >> 52) & 0x7FF;
|
|
var ey = i32(ux >> 52) & 0x7FF;
|
|
const sx = ux >> 63;
|
|
|
|
if (uy << 1 == 0 or math.isNan(y) or ex == 0x7FF) {
|
|
return (x * y) / (x * y);
|
|
}
|
|
if (ux << 1 <= uy << 1) {
|
|
if (ux << 1 == uy << 1) {
|
|
return 0 * x;
|
|
} else {
|
|
return x;
|
|
}
|
|
}
|
|
|
|
// normalize x and y
|
|
if (ex == 0) {
|
|
var i = ux << 12;
|
|
while (i >> 63 == 0) : (i <<= 1) {
|
|
ex -= 1;
|
|
}
|
|
ux <<= u64(-ex + 1);
|
|
} else {
|
|
ux &= @maxValue(u64) >> 12;
|
|
ux |= 1 << 52;
|
|
}
|
|
|
|
if (ey == 0) {
|
|
var i = uy << 12;
|
|
while (i >> 63 == 0) : (i <<= 1) {
|
|
ey -= 1;
|
|
}
|
|
uy <<= u64(-ey + 1);
|
|
} else {
|
|
uy &= @maxValue(u64) >> 12;
|
|
uy |= 1 << 52;
|
|
}
|
|
|
|
// x mod y
|
|
while (ex > ey) : (ex -= 1) {
|
|
const i = ux - uy;
|
|
if (i >> 63 == 0) {
|
|
if (i == 0) {
|
|
return 0 * x;
|
|
}
|
|
ux = i;
|
|
}
|
|
ux <<= 1;
|
|
}
|
|
{
|
|
const i = ux - uy;
|
|
if (i >> 63 == 0) {
|
|
if (i == 0) {
|
|
return 0 * x;
|
|
}
|
|
ux = i;
|
|
}
|
|
}
|
|
|
|
while (ux >> 52 == 0) : (ux <<= 1) {
|
|
ex -= 1;
|
|
}
|
|
|
|
// scale result up
|
|
if (ex > 0) {
|
|
ux -= 1 << 52;
|
|
ux |= u64(ex) << 52;
|
|
} else {
|
|
ux >>= u64(-ex + 1);
|
|
}
|
|
|
|
ux |= sx << 63;
|
|
@bitCast(f64, ux)
|
|
}
|
|
|
|
// duplicate symbol clash with `fmod` test name
|
|
test "fmod_" {
|
|
assert(fmod(f32, 1.3, 2.5) == fmod32(1.3, 2.5));
|
|
assert(fmod(f64, 1.3, 2.5) == fmod64(1.3, 2.5));
|
|
}
|
|
|
|
test "fmod32" {
|
|
const epsilon = 0.000001;
|
|
|
|
assert(math.approxEq(f32, fmod32(5.2, 2.0), 1.2, epsilon));
|
|
assert(math.approxEq(f32, fmod32(18.5, 4.2), 1.7, epsilon));
|
|
assert(math.approxEq(f32, fmod32(23, 48.34), 23.0, epsilon));
|
|
assert(math.approxEq(f32, fmod32(123.340890, 2398.2314), 123.340889, epsilon));
|
|
}
|
|
|
|
test "fmod64" {
|
|
const epsilon = 0.000001;
|
|
|
|
assert(math.approxEq(f64, fmod64(5.2, 2.0), 1.2, epsilon));
|
|
assert(math.approxEq(f64, fmod64(18.5, 4.2), 1.7, epsilon));
|
|
assert(math.approxEq(f64, fmod64(23, 48.34), 23.0, epsilon));
|
|
assert(math.approxEq(f64, fmod64(123.340890, 2398.2314), 123.340889, epsilon));
|
|
}
|