fn atanBinary64(x: f64) f64 {
const atanhi: []const f64 = &.{
4.63647609000806093515e-01, // atan(0.5)hi 0x3FDDAC67, 0x0561BB4F
7.85398163397448278999e-01, // atan(1.0)hi 0x3FE921FB, 0x54442D18
9.82793723247329054082e-01, // atan(1.5)hi 0x3FEF730B, 0xD281F69B
1.57079632679489655800e+00, // atan(inf)hi 0x3FF921FB, 0x54442D18
};
const atanlo: []const f64 = &.{
2.26987774529616870924e-17, // atan(0.5)lo 0x3C7A2B7F, 0x222F65E2
3.06161699786838301793e-17, // atan(1.0)lo 0x3C81A626, 0x33145C07
1.39033110312309984516e-17, // atan(1.5)lo 0x3C700788, 0x7AF0CBBD
6.12323399573676603587e-17, // atan(inf)lo 0x3C91A626, 0x33145C07
};
const aT: []const f64 = &.{
3.33333333333329318027e-01, // 0x3FD55555, 0x5555550D
-1.99999999998764832476e-01, // 0xBFC99999, 0x9998EBC4
1.42857142725034663711e-01, // 0x3FC24924, 0x920083FF
-1.11111104054623557880e-01, // 0xBFBC71C6, 0xFE231671
9.09088713343650656196e-02, // 0x3FB745CD, 0xC54C206E
-7.69187620504482999495e-02, // 0xBFB3B0F2, 0xAF749A6D
6.66107313738753120669e-02, // 0x3FB10D66, 0xA0D03D51
-5.83357013379057348645e-02, // 0xBFADDE2D, 0x52DEFD9A
4.97687799461593236017e-02, // 0x3FA97B4B, 0x24760DEB
-3.65315727442169155270e-02, // 0xBFA2B444, 0x2C6A6C2F
1.62858201153657823623e-02, // 0x3F90AD3A, 0xE322DA11
};
const hx: u64 = @bitCast(x);
const ix: u32 = @truncate((hx >> 32) & 0x7fffffff);
const sign = (hx >> 63) != 0;
// if |x| >= 2^66
if (ix >= 0x44100000) {
if (math.isNan(x)) {
return x;
}
const z = atanhi[3] + 0x1p-120;
return if (sign) -z else z;
}
const x_, const id: ?usize = blk: {
// |x| < 0.4375
if (ix < 0x3fdc_0000) {
// |x| < 2^(-27)
if (ix < 0x3e40_0000) {
if (ix < 0x0010_0000) {
// raise underflow for subnormal x
mem.doNotOptimizeAway(@as(f32, @floatCast(x)));
}
return x;
}
break :blk .{ x, null };
} else {
const x_ = @abs(x);
// |x| < 1.1875
if (ix < 0x3ff3_0000) {
// 7/16 <= |x| < 11/16
if (ix < 0x3fe6_0000) {
break :blk .{ (2.0 * x_ - 1.0) / (2.0 + x_), 0 };
}
// 11/16 <= |x| < 19/16
else {
break :blk .{ (x_ - 1.0) / (x_ + 1.0), 1 };
}
} else {
// |x| < 2.4375
if (ix < 0x4003_8000) {
break :blk .{ (x_ - 1.5) / (1.0 + 1.5 * x_), 2 };
}
// 2.4375 <= |x| < 2^66
else {
break :blk .{ -1.0 / x_, 3 };
}
}
}
};
// end of argument reduction
const z = x_ * x_;
const w = z * z;
// break sum from i=0 to 10 aT[i]z^(i+1) into odd and even poly
const s1 = z * (aT[0] + w * (aT[2] + w * (aT[4] + w * (aT[6] + w * (aT[8] + w * aT[10])))));
const s2 = w * (aT[1] + w * (aT[3] + w * (aT[5] + w * (aT[7] + w * aT[9]))));
if (id) |id_| {
const z_ = atanhi[id_] - (x_ * (s1 + s2) - atanlo[id_] - x_);
return if (sign) -z_ else z_;
} else {
return x_ - x_ * (s1 + s2);
}
}