mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 15:42:49 +00:00
257 lines
7.1 KiB
Zig
257 lines
7.1 KiB
Zig
// Ported from musl, which is licensed under the MIT license:
|
|
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
|
|
//
|
|
// https://git.musl-libc.org/cgit/musl/tree/src/math/atanf.c
|
|
// https://git.musl-libc.org/cgit/musl/tree/src/math/atan.c
|
|
|
|
const std = @import("../std.zig");
|
|
const math = std.math;
|
|
const mem = std.mem;
|
|
const expect = std.testing.expect;
|
|
|
|
/// Returns the arc-tangent of x.
|
|
///
|
|
/// Special Cases:
|
|
/// - atan(+-0) = +-0
|
|
/// - atan(+-inf) = +-pi/2
|
|
pub fn atan(x: anytype) @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 = [_]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 = [_]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 = [_]f32{
|
|
3.3333328366e-01,
|
|
-1.9999158382e-01,
|
|
1.4253635705e-01,
|
|
-1.0648017377e-01,
|
|
6.1687607318e-02,
|
|
};
|
|
|
|
var x = x_;
|
|
var ix: u32 = @as(u32, @bitCast(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) {
|
|
mem.doNotOptimizeAway(x * x);
|
|
}
|
|
return x;
|
|
}
|
|
id = null;
|
|
} else {
|
|
x = @abs(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 = [_]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 = [_]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 = [_]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_;
|
|
const ux: u64 = @bitCast(x);
|
|
var ix: u32 = @intCast(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 < 0x3FDC0000) {
|
|
// |x| < 2^(-27)
|
|
if (ix < 0x3E400000) {
|
|
if (ix < 0x00100000) {
|
|
mem.doNotOptimizeAway(@as(f32, @floatCast(x)));
|
|
}
|
|
return x;
|
|
}
|
|
id = null;
|
|
} else {
|
|
x = @abs(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 atan {
|
|
try expect(@as(u32, @bitCast(atan(@as(f32, 0.2)))) == @as(u32, @bitCast(atan32(0.2))));
|
|
try expect(atan(@as(f64, 0.2)) == atan64(0.2));
|
|
}
|
|
|
|
test atan32 {
|
|
const epsilon = 0.000001;
|
|
|
|
try expect(math.approxEqAbs(f32, atan32(0.2), 0.197396, epsilon));
|
|
try expect(math.approxEqAbs(f32, atan32(-0.2), -0.197396, epsilon));
|
|
try expect(math.approxEqAbs(f32, atan32(0.3434), 0.330783, epsilon));
|
|
try expect(math.approxEqAbs(f32, atan32(0.8923), 0.728545, epsilon));
|
|
try expect(math.approxEqAbs(f32, atan32(1.5), 0.982794, epsilon));
|
|
}
|
|
|
|
test atan64 {
|
|
const epsilon = 0.000001;
|
|
|
|
try expect(math.approxEqAbs(f64, atan64(0.2), 0.197396, epsilon));
|
|
try expect(math.approxEqAbs(f64, atan64(-0.2), -0.197396, epsilon));
|
|
try expect(math.approxEqAbs(f64, atan64(0.3434), 0.330783, epsilon));
|
|
try expect(math.approxEqAbs(f64, atan64(0.8923), 0.728545, epsilon));
|
|
try expect(math.approxEqAbs(f64, atan64(1.5), 0.982794, epsilon));
|
|
}
|
|
|
|
test "atan32.special" {
|
|
const epsilon = 0.000001;
|
|
|
|
try expect(math.isPositiveZero(atan32(0.0)));
|
|
try expect(math.isNegativeZero(atan32(-0.0)));
|
|
try expect(math.approxEqAbs(f32, atan32(math.inf(f32)), math.pi / 2.0, epsilon));
|
|
try expect(math.approxEqAbs(f32, atan32(-math.inf(f32)), -math.pi / 2.0, epsilon));
|
|
}
|
|
|
|
test "atan64.special" {
|
|
const epsilon = 0.000001;
|
|
|
|
try expect(math.isPositiveZero(atan64(0.0)));
|
|
try expect(math.isNegativeZero(atan64(-0.0)));
|
|
try expect(math.approxEqAbs(f64, atan64(math.inf(f64)), math.pi / 2.0, epsilon));
|
|
try expect(math.approxEqAbs(f64, atan64(-math.inf(f64)), -math.pi / 2.0, epsilon));
|
|
}
|