mirror of
https://github.com/ziglang/zig.git
synced 2024-11-29 16:42:31 +00:00
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));
|
||
|
}
|