Math Functions

Source Locations

Implementation Requirements / Goals

  • The highest priority is to be as accurate as possible, according to the C and IEEE 754 standards. By default, we will aim to be correctly rounded for all rounding modes. The current rounding mode of the floating point environment is used to perform computations and produce the final results.
    • To test for correctness, we compare the outputs with other correctly rounded multiple-precision math libraries such as the GNU MPFR library or the CORE-MATH library.
  • Our next requirement is that the outputs are consistent across all platforms. Notice that the consistency requirement will be satisfied automatically if the implementation is correctly rounded.
  • Our last requirement for the implementations is to have good and predicable performance:
    • The average performance should be comparable to other libc implementations.
    • The worst case performance should be within 10X-20X of the average.
    • Platform-specific implementations or instructions could be added whenever it makes sense and provides significant performance boost.
  • For other use cases that have strict requirements on the code size, memory footprint, or latency, such as embedded systems, we will aim to be as accurate as possible within the memory or latency budgets, and consistent across all platforms.

Add a new math function to LLVM libc

Implementation Status

Basic Operations

<Func> <Func_f> (float) <Func> (double) <Func_l> (long double)
ceil XA XA XA
copysign XA XA XA
fabs XA XA XA
fdim XA XA XA
floor XA XA XA
fmax XA XA XA
fmin XA XA XA
fmod XA XA  
fpclassify      
frexp XA XA XA
ilogb XA XA XA
isfinite      
isgreater      
isgreaterequal      
isinf      
isless      
islessequal      
islessgreater      
isnan      
isnormal      
isubordered      
ldexp XA XA XA
llrint XA XA XA
llround XA XA XA
logb XA XA XA
lrint XA XA XA
lround XA XA XA
modf XA XA XA
nan      
nearbyint XA XA XA
nextafter XA XA XA
nexttoward      
remainder XA XA XA
remquo XA XA XA
rint XA XA XA
round XA XA XA
scalbn      
signbit      
trunc XA XA XA

Higher Math Functions

<Func> <Func_f> (float) <Func> (double) <Func_l> (long double)
acos XA    
acosh XA    
asin XA    
asinh XA    
atan XA    
atan2      
atanh XA    
cbrt      
cos XA XA  
cosh XA    
erf      
erfc      
exp XA    
exp10 XA    
exp2 XA    
expm1 XA    
fma XA XA  
hypot XA XA  
lgamma      
log XA    
log10 XA XA  
log1p XA    
log2 XA    
pow      
sin XA XA  
sincos XA XA  
sinh XA    
sqrt XA XA XA
tan XA    
tanh XA    
tgamma      

Accuracy of Higher Math Functions

<Func> <Func_f> (float) <Func> (double) <Func_l> (long double)
acos XA    
acosh XA    
asin XA    
asinh XA    
atan XA    
atanh XA    
cos XA large  
cosh XA    
exp XA    
exp10 XA    
exp2 XA    
expm1 XA    
fma XA XA  
hypot XA XA  
log XA    
log10 XA XA  
log1p XA    
log2 XA    
sin XA large  
sincos XA large  
sinh XA    
sqrt XA XA XA
tan XA    
tanh XA    

Legends:

  • X = x86_64, A = aarch64, a = arm32
  • Green text (eg. XA): correctly rounded for all 4 rounding modes.
  • CR: correctly rounded for the default rounding mode (round-to-the-nearest, tie-to-even).
  • x ULPs: largest errors recorded.

Performance

  • Simple performance testings are located at: libc/test/src/math/differential_testing.
  • We also use the perf tool from the CORE-MATH project: link. The performance results from the CORE-MATH’s perf tool are reported in the table below, using the system library as reference (such as the GNU C library on Linux). Fmod performance results obtained with “differential_testing”.
<Func> Reciprocal throughput (ns) Latency (ns) Testing ranges Testing configuration
LLVM libc Reference (glibc) LLVM libc Reference (glibc) CPU OS Compiler Special flags
acosf 24 29 62 77 \([-1, 1]\) Ryzen 1700 Ubuntu 22.04 LTS x86_64 Clang 14.0.0 FMA
acoshf 18 26 73 74 \([1, 21]\) Ryzen 1700 Ubuntu 22.04 LTS x86_64 Clang 14.0.0 FMA
asinf 23 27 62 62 \([-1, 1]\) Ryzen 1700 Ubuntu 22.04 LTS x86_64 Clang 14.0.0 FMA
asinhf 21 39 77 91 \([-10, 10]\) Ryzen 1700 Ubuntu 22.04 LTS x86_64 Clang 14.0.0 FMA
atanf 27 29 79 68 \([-10, 10]\) Ryzen 1700 Ubuntu 22.04 LTS x86_64 Clang 14.0.0 FMA
atanhf 18 66 68 133 \([-1, 1]\) Ryzen 1700 Ubuntu 22.04 LTS x86_64 Clang 14.0.0 FMA
cosf 13 32 53 59 \([0, 2\pi]\) Ryzen 1700 Ubuntu 20.04 LTS x86_64 Clang 12.0.0 FMA
coshf 14 20 50 48 \([-10, 10]\) Ryzen 1700 Ubuntu 22.04 LTS x86_64 Clang 14.0.0 FMA
expf 9 7 44 38 \([-10, 10]\) Ryzen 1700 Ubuntu 20.04 LTS x86_64 Clang 12.0.0 FMA
exp10f 10 8 40 38 \([-10, 10]\) Ryzen 1700 Ubuntu 22.04 LTS x86_64 Clang 14.0.0 FMA
exp2f 9 6 35 31 \([-10, 10]\) Ryzen 1700 Ubuntu 22.04 LTS x86_64 Clang 14.0.0 FMA
expm1f 9 44 42 121 \([-10, 10]\) Ryzen 1700 Ubuntu 20.04 LTS x86_64 Clang 12.0.0 FMA
fmodf 73 263
[MIN_NORMAL, MAX_NORMAL] i5 mobile Ubuntu 20.04 LTS x86_64 Clang 12.0.0  
9 11
[0, MAX_SUBNORMAL] i5 mobile Ubuntu 20.04 LTS x86_64 Clang 12.0.0  
fmod 595 3297
[MIN_NORMAL, MAX_NORMAL] i5 mobile Ubuntu 20.04 LTS x86_64 Clang 12.0.0  
14 13
[0, MAX_SUBNORMAL] i5 mobile Ubuntu 20.04 LTS x86_64 Clang 12.0.0  
hypotf 25 15 64 49 \([-10, 10] \times [-10, 10]\) Ryzen 1700 Ubuntu 20.04 LTS x86_64 Clang 12.0.0  
logf 12 10 56 46 \([e^{-1}, e]\) Ryzen 1700 Ubuntu 20.04 LTS x86_64 Clang 12.0.0 FMA
log10f 13 25 57 72 \([e^{-1}, e]\) Ryzen 1700 Ubuntu 20.04 LTS x86_64 Clang 12.0.0 FMA
log1pf 16 33 61 97 \([e^{-0.5} - 1, e^{0.5} - 1]\) Ryzen 1700 Ubuntu 20.04 LTS x86_64 Clang 12.0.0 FMA
log2f 13 10 57 46 \([e^{-1}, e]\) Ryzen 1700 Ubuntu 20.04 LTS x86_64 Clang 12.0.0 FMA
sinf 12 25 51 57 \([-\pi, \pi]\) Ryzen 1700 Ubuntu 20.04 LTS x86_64 Clang 12.0.0 FMA
sincosf 19 30 57 68 \([-\pi, \pi]\) Ryzen 1700 Ubuntu 20.04 LTS x86_64 Clang 12.0.0 FMA
sinhf 13 63 48 137 \([-10, 10]\) Ryzen 1700 Ubuntu 22.04 LTS x86_64 Clang 14.0.0 FMA
tanf 16 50 61 107 \([-\pi, \pi]\) Ryzen 1700 Ubuntu 22.04 LTS x86_64 Clang 14.0.0 FMA
tanhf 13 55 57 123 \([-10, 10]\) Ryzen 1700 Ubuntu 22.04 LTS x86_64 Clang 14.0.0 FMA