zig/lib/compiler_rt/mulc3.zig
Cody Tapscott 05915b85dd compiler-rt: Implement mulXc3 and divXc3 functions
These are the standard complex multiplication/division functions
required by the C standard (Annex G).

Don't get me started on the standard's handling of complex-infinity...
2022-10-09 11:09:41 -07:00

80 lines
2.2 KiB
Zig

const std = @import("std");
const isNan = std.math.isNan;
const isInf = std.math.isInf;
const copysign = std.math.copysign;
pub fn Complex(comptime T: type) type {
return extern struct {
real: T,
imag: T,
};
}
/// Implementation based on Annex G of C17 Standard (N2176)
pub inline fn mulc3(comptime T: type, a_in: T, b_in: T, c_in: T, d_in: T) Complex(T) {
var a = a_in;
var b = b_in;
var c = c_in;
var d = d_in;
const ac = a * c;
const bd = b * d;
const ad = a * d;
const bc = b * c;
const zero: T = 0.0;
const one: T = 1.0;
var z = Complex(T){
.real = ac - bd,
.imag = ad + bc,
};
if (isNan(z.real) and isNan(z.imag)) {
var recalc: bool = false;
if (isInf(a) or isInf(b)) { // (a + ib) is infinite
// "Box" the infinity (+/-inf goes to +/-1, all finite values go to 0)
a = copysign(if (isInf(a)) one else zero, a);
b = copysign(if (isInf(b)) one else zero, b);
// Replace NaNs in the other factor with (signed) 0
if (isNan(c)) c = copysign(zero, c);
if (isNan(d)) d = copysign(zero, d);
recalc = true;
}
if (isInf(c) or isInf(d)) { // (c + id) is infinite
// "Box" the infinity (+/-inf goes to +/-1, all finite values go to 0)
c = copysign(if (isInf(c)) one else zero, c);
d = copysign(if (isInf(d)) one else zero, d);
// Replace NaNs in the other factor with (signed) 0
if (isNan(a)) a = copysign(zero, a);
if (isNan(b)) b = copysign(zero, b);
recalc = true;
}
if (!recalc and (isInf(ac) or isInf(bd) or isInf(ad) or isInf(bc))) {
// Recover infinities from overflow by changing NaNs to 0
if (isNan(a)) a = copysign(zero, a);
if (isNan(b)) b = copysign(zero, b);
if (isNan(c)) c = copysign(zero, c);
if (isNan(d)) d = copysign(zero, d);
recalc = true;
}
if (recalc) {
return .{
.real = std.math.inf(T) * (a * c - b * d),
.imag = std.math.inf(T) * (a * d + b * c),
};
}
}
return z;
}