mirror of
https://github.com/ziglang/zig.git
synced 2025-12-12 09:13:11 +00:00
109 lines
3.1 KiB
Zig
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");
|
|
}
|