You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The double-precision exp functions in Sleef appear to return infinity at slightly too low a value.
The current cutoff input above which Sleef returns infinity is: 709.78271114955742909217217426
However, 709.78271289338399673222338991 seems to be a better cutoff.
The cutoff Sleef uses is equal to log(1.7976900000001013e+308), whereas the cutoff I propose is: log(1.79769313486231570815e+308) aka log(DBL_MAX)
There are 15.3 million double-precision values between Sleef's cutoff and the one I propose. I tested all of those values on x86 SSE4 and AVX2 with the constant swapped out, and it appears that all of the return values are accurate to within 1 ULP (using expl with long double precision as the reference). This constant also exists in the pow functions where I believe it is similarly set too low, but I'm not sure how to perform exhaustive testing to confirm raising the constant is okay.
The specific line of code relevant is here, though I believe the constant should probably be adjusted in all 4 places it appears.
Testing code I used to check accuracy after adjusting the constant
#include<stdint.h>#include<stdio.h>#include<string.h>#include<math.h>#include"sleef.h"//cmake -B build -S . -G Ninja//cmake --build build//gcc -o sleef_exp_example sleef_exp_example.c -lsleef -lm -L./build/lib -I ./build/include -march=nativedoubleulp_diff(long doubleexpected, doubleactual);
size_tbitcast_u64(doublex);
doublebitcast_f64(size_tx);
voidcheckAccuracy(double*input, double*actual, long double*expected, size_tN, doublemax_ulp);
// Problematic constant: 709.78271114955742909217217426// Improved constant: 709.78271289338399673222338991intmain() {
size_tstart=bitcast_u64(709.78271114955742909217217426) -2;
size_tend=bitcast_u64(709.78271289338399673222338991) +2;
size_tN= (((end-start) +7)/8) *8; // Round to nearest multiple of 8double*inputs=malloc(N*sizeof(double));
double*outputs=malloc(N*sizeof(double));
long double*expected=malloc(N*sizeof(long double));
for (size_ti=0; i<N; i++) {
inputs[i] =bitcast_f64(start+i);
}
printf("Make %zu inputs from %.20g to %.20g\n", N, inputs[0], inputs[N-1]);
for (size_ti=0; i<N; i+=1) {
expected[i] =expl((long double) inputs[i]);
}
printf("Sleef_expd4_u10avx2:\n");
for (size_ti=0; i<N; i+=4) {
__m256dx=_mm256_loadu_pd(inputs+i);
x=Sleef_expd4_u10avx2(x);
_mm256_storeu_pd(outputs+i, x);
}
checkAccuracy(inputs, outputs, expected, N, 1.0);
printf("Sleef_expd2_u10sse4:\n");
for (size_ti=0; i<N; i++) outputs[i] =-1;
for (size_ti=0; i<N; i+=2) {
__m128dx=_mm_loadu_pd(inputs+i);
x=Sleef_expd2_u10sse4(x);
_mm_storeu_pd(outputs+i, x);
}
checkAccuracy(inputs, outputs, expected, N, 1.0);
}
doubleulp_diff(long doubleexpected, doubleactual) {
if (isinf(actual) && ((double) expected) ==actual) return0;
long doublediff=fabsl(expected- ((long double) actual));
// Calculate lowerp-precision expected1 and expected2 that straddle// the actual value of expecteddoubleexpected1=expected;
doubleexpected2=nextafter(expected1, (expected1<expected ? 1 : 1) *INFINITY);
doubleulp=fabsl(((long double) expected1) - ((long double) expected2));
returndiff / ulp;
}
size_tbitcast_u64(doublex) {
size_ty;
memcpy(&y, &x, sizeof(double));
returny;
}
doublebitcast_f64(size_tx) {
doubley;
memcpy(&y, &x, sizeof(double));
returny;
}
voidcheckAccuracy(double*input, double*actual, long double*expected, size_tN, doublemax_ulp) {
doubleworst_ulp=0.0;
intinfinite_outputs=0;
for (size_ti=0; i<N; i++) {
if (isinf((double) expected[i])) {
infinite_outputs+=1;
}
doubleulp=ulp_diff(expected[i], actual[i]);
worst_ulp=ulp>worst_ulp ? ulp : worst_ulp;
if (ulp>max_ulp||isnan(ulp)) {
printf("ulp=%.20g (in=%a, out=%a) (expected=%La (%a))\n", ulp, input[i], actual[i], expected[i], (double) expected[i]);
return;
}
}
printf("All within %f ULP (worst observed=%.20g, infinite outputs=%d)\n", max_ulp, worst_ulp, infinite_outputs);
}
The text was updated successfully, but these errors were encountered:
The double-precision exp functions in Sleef appear to return infinity at slightly too low a value.
The current cutoff input above which Sleef returns infinity is:
709.78271114955742909217217426
However,
709.78271289338399673222338991
seems to be a better cutoff.The cutoff Sleef uses is equal to
log(1.7976900000001013e+308)
, whereas the cutoff I propose is:log(1.79769313486231570815e+308)
akalog(DBL_MAX)
There are 15.3 million double-precision values between Sleef's cutoff and the one I propose. I tested all of those values on x86 SSE4 and AVX2 with the constant swapped out, and it appears that all of the return values are accurate to within 1 ULP (using
expl
withlong double
precision as the reference). This constant also exists in thepow
functions where I believe it is similarly set too low, but I'm not sure how to perform exhaustive testing to confirm raising the constant is okay.The specific line of code relevant is here, though I believe the constant should probably be adjusted in all 4 places it appears.
Testing code I used to check accuracy after adjusting the constant
The text was updated successfully, but these errors were encountered: