252 lines
6.7 KiB
Zig
252 lines
6.7 KiB
Zig
// Special Cases:
|
|
//
|
|
// - atan(+-0) = +-0
|
|
// - atan(+-inf) = +-pi/2
|
|
|
|
const std = @import("../index.zig");
|
|
const math = std.math;
|
|
const assert = std.debug.assert;
|
|
|
|
pub fn atan(x: var) @typeOf(x) {
|
|
const T = @typeOf(x);
|
|
return switch (T) {
|
|
f32 => atan32(x),
|
|
f64 => atan64(x),
|
|
else => @compileError("atan not implemented for " ++ @typeName(T)),
|
|
};
|
|
}
|
|
|
|
fn atan32(x_: f32) f32 {
|
|
const atanhi = []const f32 {
|
|
4.6364760399e-01, // atan(0.5)hi
|
|
7.8539812565e-01, // atan(1.0)hi
|
|
9.8279368877e-01, // atan(1.5)hi
|
|
1.5707962513e+00, // atan(inf)hi
|
|
};
|
|
|
|
const atanlo = []const f32 {
|
|
5.0121582440e-09, // atan(0.5)lo
|
|
3.7748947079e-08, // atan(1.0)lo
|
|
3.4473217170e-08, // atan(1.5)lo
|
|
7.5497894159e-08, // atan(inf)lo
|
|
};
|
|
|
|
const aT = []const f32 {
|
|
3.3333328366e-01,
|
|
-1.9999158382e-01,
|
|
1.4253635705e-01,
|
|
-1.0648017377e-01,
|
|
6.1687607318e-02,
|
|
};
|
|
|
|
var x = x_;
|
|
var ix: u32 = @bitCast(u32, x);
|
|
const sign = ix >> 31;
|
|
ix &= 0x7FFFFFFF;
|
|
|
|
// |x| >= 2^26
|
|
if (ix >= 0x4C800000) {
|
|
if (math.isNan(x)) {
|
|
return x;
|
|
} else {
|
|
const z = atanhi[3] + 0x1.0p-120;
|
|
return if (sign != 0) -z else z;
|
|
}
|
|
}
|
|
|
|
var id: ?usize = undefined;
|
|
|
|
// |x| < 0.4375
|
|
if (ix < 0x3EE00000) {
|
|
// |x| < 2^(-12)
|
|
if (ix < 0x39800000) {
|
|
if (ix < 0x00800000) {
|
|
math.forceEval(x * x);
|
|
}
|
|
return x;
|
|
}
|
|
id = null;
|
|
} else {
|
|
x = math.fabs(x);
|
|
// |x| < 1.1875
|
|
if (ix < 0x3F980000) {
|
|
// 7/16 <= |x| < 11/16
|
|
if (ix < 0x3F300000) {
|
|
id = 0;
|
|
x = (2.0 * x - 1.0) / (2.0 + x);
|
|
}
|
|
// 11/16 <= |x| < 19/16
|
|
else {
|
|
id = 1;
|
|
x = (x - 1.0) / (x + 1.0);
|
|
}
|
|
}
|
|
else {
|
|
// |x| < 2.4375
|
|
if (ix < 0x401C0000) {
|
|
id = 2;
|
|
x = (x - 1.5) / (1.0 + 1.5 * x);
|
|
}
|
|
// 2.4375 <= |x| < 2^26
|
|
else {
|
|
id = 3;
|
|
x = -1.0 / x;
|
|
}
|
|
}
|
|
}
|
|
|
|
const z = x * x;
|
|
const w = z * z;
|
|
const s1 = z * (aT[0] + w * (aT[2] + w * aT[4]));
|
|
const s2 = w * (aT[1] + w * aT[3]);
|
|
|
|
if (id) |id_value| {
|
|
const zz = atanhi[id_value] - ((x * (s1 + s2) - atanlo[id_value]) - x);
|
|
return if (sign != 0) -zz else zz;
|
|
} else {
|
|
return x - x * (s1 + s2);
|
|
}
|
|
}
|
|
|
|
fn atan64(x_: f64) f64 {
|
|
const atanhi = []const f64 {
|
|
4.63647609000806093515e-01, // atan(0.5)hi
|
|
7.85398163397448278999e-01, // atan(1.0)hi
|
|
9.82793723247329054082e-01, // atan(1.5)hi
|
|
1.57079632679489655800e+00, // atan(inf)hi
|
|
};
|
|
|
|
const atanlo = []const f64 {
|
|
2.26987774529616870924e-17, // atan(0.5)lo
|
|
3.06161699786838301793e-17, // atan(1.0)lo
|
|
1.39033110312309984516e-17, // atan(1.5)lo
|
|
6.12323399573676603587e-17, // atan(inf)lo
|
|
};
|
|
|
|
const aT = []const f64 {
|
|
3.33333333333329318027e-01,
|
|
-1.99999999998764832476e-01,
|
|
1.42857142725034663711e-01,
|
|
-1.11111104054623557880e-01,
|
|
9.09088713343650656196e-02,
|
|
-7.69187620504482999495e-02,
|
|
6.66107313738753120669e-02,
|
|
-5.83357013379057348645e-02,
|
|
4.97687799461593236017e-02,
|
|
-3.65315727442169155270e-02,
|
|
1.62858201153657823623e-02,
|
|
};
|
|
|
|
var x = x_;
|
|
var ux = @bitCast(u64, x);
|
|
var ix = u32(ux >> 32);
|
|
const sign = ix >> 31;
|
|
ix &= 0x7FFFFFFF;
|
|
|
|
// |x| >= 2^66
|
|
if (ix >= 0x44100000) {
|
|
if (math.isNan(x)) {
|
|
return x;
|
|
} else {
|
|
const z = atanhi[3] + 0x1.0p-120;
|
|
return if (sign != 0) -z else z;
|
|
}
|
|
}
|
|
|
|
var id: ?usize = undefined;
|
|
|
|
// |x| < 0.4375
|
|
if (ix < 0x3DFC0000) {
|
|
// |x| < 2^(-27)
|
|
if (ix < 0x3E400000) {
|
|
if (ix < 0x00100000) {
|
|
math.forceEval(f32(x));
|
|
}
|
|
return x;
|
|
}
|
|
id = null;
|
|
} else {
|
|
x = math.fabs(x);
|
|
// |x| < 1.1875
|
|
if (ix < 0x3FF30000) {
|
|
// 7/16 <= |x| < 11/16
|
|
if (ix < 0x3FE60000) {
|
|
id = 0;
|
|
x = (2.0 * x - 1.0) / (2.0 + x);
|
|
}
|
|
// 11/16 <= |x| < 19/16
|
|
else {
|
|
id = 1;
|
|
x = (x - 1.0) / (x + 1.0);
|
|
}
|
|
}
|
|
else {
|
|
// |x| < 2.4375
|
|
if (ix < 0x40038000) {
|
|
id = 2;
|
|
x = (x - 1.5) / (1.0 + 1.5 * x);
|
|
}
|
|
// 2.4375 <= |x| < 2^66
|
|
else {
|
|
id = 3;
|
|
x = -1.0 / x;
|
|
}
|
|
}
|
|
}
|
|
|
|
const z = x * x;
|
|
const w = z * z;
|
|
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_value| {
|
|
const zz = atanhi[id_value] - ((x * (s1 + s2) - atanlo[id_value]) - x);
|
|
return if (sign != 0) -zz else zz;
|
|
} else {
|
|
return x - x * (s1 + s2);
|
|
}
|
|
}
|
|
|
|
test "math.atan" {
|
|
assert(@bitCast(u32, atan(f32(0.2))) == @bitCast(u32, atan32(0.2)));
|
|
assert(atan(f64(0.2)) == atan64(0.2));
|
|
}
|
|
|
|
test "math.atan32" {
|
|
const epsilon = 0.000001;
|
|
|
|
assert(math.approxEq(f32, atan32(0.2), 0.197396, epsilon));
|
|
assert(math.approxEq(f32, atan32(-0.2), -0.197396, epsilon));
|
|
assert(math.approxEq(f32, atan32(0.3434), 0.330783, epsilon));
|
|
assert(math.approxEq(f32, atan32(0.8923), 0.728545, epsilon));
|
|
assert(math.approxEq(f32, atan32(1.5), 0.982794, epsilon));
|
|
}
|
|
|
|
test "math.atan64" {
|
|
const epsilon = 0.000001;
|
|
|
|
assert(math.approxEq(f64, atan64(0.2), 0.197396, epsilon));
|
|
assert(math.approxEq(f64, atan64(-0.2), -0.197396, epsilon));
|
|
assert(math.approxEq(f64, atan64(0.3434), 0.330783, epsilon));
|
|
assert(math.approxEq(f64, atan64(0.8923), 0.728545, epsilon));
|
|
assert(math.approxEq(f64, atan64(1.5), 0.982794, epsilon));
|
|
}
|
|
|
|
test "math.atan32.special" {
|
|
const epsilon = 0.000001;
|
|
|
|
assert(atan32(0.0) == 0.0);
|
|
assert(atan32(-0.0) == -0.0);
|
|
assert(math.approxEq(f32, atan32(math.inf(f32)), math.pi / 2.0, epsilon));
|
|
assert(math.approxEq(f32, atan32(-math.inf(f32)), -math.pi / 2.0, epsilon));
|
|
}
|
|
|
|
test "math.atan64.special" {
|
|
const epsilon = 0.000001;
|
|
|
|
assert(atan64(0.0) == 0.0);
|
|
assert(atan64(-0.0) == -0.0);
|
|
assert(math.approxEq(f64, atan64(math.inf(f64)), math.pi / 2.0, epsilon));
|
|
assert(math.approxEq(f64, atan64(-math.inf(f64)), -math.pi / 2.0, epsilon));
|
|
}
|