2022-04-25 17:21:05 -07:00

109 lines
3.1 KiB
Zig

const builtin = @import("builtin");
const std = @import("std");
const math = std.math;
const normalize = @import("divdf3.zig").normalize;
// fmodx - floating modulo large, returns the remainder of division for f80 types
// Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits
pub fn fmodx(a: f80, b: f80) callconv(.C) f80 {
@setRuntimeSafety(builtin.is_test);
const T = f80;
const Z = std.meta.Int(.unsigned, @bitSizeOf(T));
const significandBits = math.floatMantissaBits(T);
const fractionalBits = math.floatFractionalBits(T);
const exponentBits = math.floatExponentBits(T);
const signBit = (@as(Z, 1) << (significandBits + exponentBits));
const maxExponent = ((1 << exponentBits) - 1);
var aRep = @bitCast(Z, a);
var bRep = @bitCast(Z, b);
const signA = aRep & signBit;
var expA = @intCast(i32, (@bitCast(Z, a) >> significandBits) & maxExponent);
var expB = @intCast(i32, (@bitCast(Z, b) >> significandBits) & maxExponent);
// There are 3 cases where the answer is undefined, check for:
// - fmodx(val, 0)
// - fmodx(val, NaN)
// - fmodx(inf, val)
// The sign on checked values does not matter.
// Doing (a * b) / (a * b) procudes undefined results
// because the three cases always produce undefined calculations:
// - 0 / 0
// - val * NaN
// - inf / inf
if (b == 0 or math.isNan(b) or expA == maxExponent) {
return (a * b) / (a * b);
}
// Remove the sign from both
aRep &= ~signBit;
bRep &= ~signBit;
if (aRep <= bRep) {
if (aRep == bRep) {
return 0 * a;
}
return a;
}
if (expA == 0) expA = normalize(f80, &aRep);
if (expB == 0) expB = normalize(f80, &bRep);
var highA: u64 = 0;
var highB: u64 = 0;
var lowA: u64 = @truncate(u64, aRep);
var lowB: u64 = @truncate(u64, bRep);
while (expA > expB) : (expA -= 1) {
var high = highA -% highB;
var low = lowA -% lowB;
if (lowA < lowB) {
high -%= 1;
}
if (high >> 63 == 0) {
if ((high | low) == 0) {
return 0 * a;
}
highA = 2 *% high + (low >> 63);
lowA = 2 *% low;
} else {
highA = 2 *% highA + (lowA >> 63);
lowA = 2 *% lowA;
}
}
var high = highA -% highB;
var low = lowA -% lowB;
if (lowA < lowB) {
high -%= 1;
}
if (high >> 63 == 0) {
if ((high | low) == 0) {
return 0 * a;
}
highA = high;
lowA = low;
}
while ((lowA >> fractionalBits) == 0) {
lowA = 2 *% lowA;
expA = expA - 1;
}
// Combine the exponent with the sign and significand, normalize if happened to be denormalized
if (expA < -fractionalBits) {
return @bitCast(T, signA);
} else if (expA <= 0) {
return @bitCast(T, (lowA >> @intCast(math.Log2Int(u64), 1 - expA)) | signA);
} else {
return @bitCast(T, lowA | (@as(Z, @intCast(u16, expA)) << significandBits) | signA);
}
}
test {
_ = @import("fmodx_test.zig");
}