Math Functions in LLVM-libc

Summary

  • This document tracks the status of the implementation of math functions in LLVM libc.

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.

Source Locations

Add a new math function to LLVM libc

Implementation Status

Basic Operations

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

Higher Math Functions

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

Accuracy of Higher Math Functions

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

Legends:

  • : 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
asinf 23 27 62 62 \([-1, 1]\) 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 20 66 71 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 19 50 82 107 \([-\pi, \pi]\) Ryzen 1700 Ubuntu 20.04 LTS x86_64 Clang 12.0.0 FMA
tanhf 13 55 57 123 \([-10, 10]\) Ryzen 1700 Ubuntu 22.04 LTS x86_64 Clang 14.0.0 FMA