Skip to content

Commit d3388b6

Browse files
committed
AK: Make atan() more precise and faster on non-x86
Instead of calling asin(), approximate atan() directly. Takes the biggest error of atan() over 0..2*pi (which is an arbitrary interval, given that atan()'s domain is all reals) from 7.15256e-07 to 1.19209e-07. That's enough to make TestPDF pass when run inside serenity (part of SerenityOS#25934). It also takes bench_trig_atan* from: Running benchmark 'bench_trig_atan'. Completed benchmark 'bench_trig_atan' in 181ms Running benchmark 'bench_trig_atanf'. Completed benchmark 'bench_trig_atanf' in 188ms to Running benchmark 'bench_trig_atan'. Completed benchmark 'bench_trig_atan' in 123ms Running benchmark 'bench_trig_atanf'. Completed benchmark 'bench_trig_atanf' in 72ms See the PR adding this commit for details on the program that computed the precision. (I first tried making asin() more precise, but even with a more precise asin(), atan()'s error never went below 7.15256e-07 when it was implemented in terms of asin(). Possibly because larger values all map to arguments close to 1.0 for asin().)
1 parent d59af74 commit d3388b6

File tree

1 file changed

+44
-1
lines changed

1 file changed

+44
-1
lines changed

AK/Math.h

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,50 @@ constexpr T atan(T value)
899899
return ret;
900900
#else
901901
# if defined(AK_OS_SERENITY)
902-
return asin(value / sqrt(1 + value * value));
902+
if (value < 0)
903+
return -atan(-value);
904+
905+
if (value > 1)
906+
return Pi<T> / 2 - atan(1 / value);
907+
908+
// atan is an odd function a leading factor of 1, so this uses the same substitutions as sin().
909+
// See there for a description.
910+
auto f = [](T x) {
911+
if constexpr (IsSame<T, f32>) {
912+
// lolremez --float --degree 7 --range "1e-50:1" "(atan(sqrt(x))-sqrt(x))/(x*sqrt(x))" "1/(x*sqrt(x))"
913+
// "Estimated max error: 7.351572e-9"
914+
float u = 0.0026222446f;
915+
u = u * x + -0.015132537f;
916+
u = u * x + 0.041121863f;
917+
u = u * x + -0.073667064f;
918+
u = u * x + 0.10573932f;
919+
u = u * x + -0.14185975f;
920+
u = u * x + 0.19990396f;
921+
return u * x + -0.33332986f;
922+
} else {
923+
// FIXME: Could do something custom for long double.
924+
// lolremez --degree 15 --range "1e-50:1" "(atan(sqrt(x))-sqrt(x))/(x*sqrt(x))" "1/(x*sqrt(x))"
925+
// "Estimated max error: 2.695747111292741e-15"
926+
T u = 6.4855700791782353e-05;
927+
u = u * x + -0.00062980993515420608;
928+
u = u * x + 0.0028877745234626882;
929+
u = u * x + -0.0083913659122280861;
930+
u = u * x + 0.01759373283992496;
931+
u = u * x + -0.028943865588822337;
932+
u = u * x + 0.04001711781175539;
933+
u = u * x + -0.049493567473208426;
934+
u = u * x + 0.05782092815821073;
935+
u = u * x + -0.066423996784058609;
936+
u = u * x + 0.076879768543915233;
937+
u = u * x + -0.090903598304650779;
938+
u = u * x + 0.11111064186237864;
939+
u = u * x + -0.14285711801574916;
940+
u = u * x + 0.19999999929660117;
941+
return u * x + -0.33333333332571796;
942+
}
943+
};
944+
T squared = value * value;
945+
return value + value * squared * f(squared);
903946
# endif
904947
return __builtin_atan(value);
905948
#endif

0 commit comments

Comments
 (0)