/
githubmirror
/
cmssw
Обзор
Документация
Войти
/
githubmirror
/
cmssw
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
DataFormats/Math/interface/approx_log.h
151 строка
5 KB
Shahzad Malik Muzaffar
use max instead if infinity which hasundefined behavior
18 июл 2024, 13:28
18 июл 2024, 13:28
3d04e1f
Код
Авторство
О чём код?
#ifndef DataFormatsMathAPPROX_LOG_H #define DataFormatsMathAPPROX_LOG_H /* Quick and dirty, branchless, log implementations Author: Florent de Dinechin, Aric, ENS-Lyon All right reserved Warning + disclaimers: - no special case handling (infinite/NaN inputs, even zero input, etc) - no input subnormal handling, you'll get completely wrong results. This is the worst problem IMHO (leading to very rare but very bad bugs) However it is probable you can guarantee that your input numbers are never subnormal, check that. Otherwise I'll fix it... - output accuracy reported is only absolute. Relative accuracy may be arbitrary bad around log(1), especially for approx_log0. approx_logf is more or less OK. - The larger/smaller the input x (i.e. away from 1), the better the accuracy. - For the higher degree polynomials it is possible to win a few cycles by parallelizing the evaluation of the polynomial (Estrin). It doesn't make much sense if you want to make a vector function. - All this code is FMA-safe (and accelerated by FMA) Feel free to distribute or insert in other programs etc, as long as this notice is attached. Comments, requests etc: Florent.de.Dinechin@ens-lyon.fr Polynomials were obtained using Sollya scripts (in comments): please also keep these comments attached to the code of approx_logf. */ #include "DataFormats/Math/interface/approx_math.h" template <int DEGREE> constexpr float approx_logf_P(float p); // the following is Sollya output // degree = 2 => absolute accuracy is 7 bits template <> constexpr float approx_logf_P<2>(float y) { return y * (float(0x1.0671c4p0) + y * (float(-0x7.27744p-4))); } // degree = 3 => absolute accuracy is 10 bits template <> constexpr float approx_logf_P<3>(float y) { return y * (float(0x1.013354p0) + y * (-float(0x8.33006p-4) + y * float(0x4.0d16cp-4))); } // degree = 4 => absolute accuracy is 13 bits template <> constexpr float approx_logf_P<4>(float y) { return y * (float(0xf.ff5bap-4) + y * (-float(0x8.13e5ep-4) + y * (float(0x5.826ep-4) + y * (-float(0x2.e87fb8p-4))))); } // degree = 5 => absolute accuracy is 16 bits template <> constexpr float approx_logf_P<5>(float y) { return y * (float(0xf.ff652p-4) + y * (-float(0x8.0048ap-4) + y * (float(0x5.72782p-4) + y * (-float(0x4.20904p-4) + y * float(0x2.1d7fd8p-4))))); } // degree = 6 => absolute accuracy is 19 bits template <> constexpr float approx_logf_P<6>(float y) { return y * (float(0xf.fff14p-4) + y * (-float(0x7.ff4bfp-4) + y * (float(0x5.582f6p-4) + y * (-float(0x4.1dcf2p-4) + y * (float(0x3.3863f8p-4) + y * (-float(0x1.9288d4p-4))))))); } // degree = 7 => absolute accuracy is 21 bits template <> constexpr float approx_logf_P<7>(float y) { return y * (float(0x1.000034p0) + y * (-float(0x7.ffe57p-4) + y * (float(0x5.5422ep-4) + y * (-float(0x4.037a6p-4) + y * (float(0x3.541c88p-4) + y * (-float(0x2.af842p-4) + y * float(0x1.48b3d8p-4))))))); } // degree = 8 => absolute accuracy is 24 bits template <> constexpr float approx_logf_P<8>(float y) { return y * (float(0x1.00000cp0) + y * (float(-0x8.0003p-4) + y * (float(0x5.55087p-4) + y * (float(-0x3.fedcep-4) + y * (float(0x3.3a1dap-4) + y * (float(-0x2.cb55fp-4) + y * (float(0x2.38831p-4) + y * (float(-0xf.e87cap-8))))))))); } template <int DEGREE> constexpr float unsafe_logf_impl(float x) { using namespace approx_math; binary32 xx, m; xx.f = x; // as many integer computations as possible, most are 1-cycle only, and lots of ILP. int e = (((xx.i32) >> 23) & 0xFF) - 127; // extract exponent m.i32 = (xx.i32 & 0x007FFFFF) | 0x3F800000; // extract mantissa as an FP number int adjust = (xx.i32 >> 22) & 1; // first bit of the mantissa, tells us if 1.m > 1.5 m.i32 -= adjust << 23; // if so, divide 1.m by 2 (exact operation, no rounding) e += adjust; // and update exponent so we still have x=2^E*y // now back to floating-point float y = m.f - 1.0f; // Sterbenz-exact; cancels but we don't care about output relative error // all the computations so far were free of rounding errors... // the following is based on Sollya output float p = approx_logf_P<DEGREE>(y); constexpr float Log2 = 0xb.17218p-4; // 0.693147182464599609375 return float(e) * Log2 + p; } #ifndef NO_APPROX_MATH template <int DEGREE> constexpr float unsafe_logf(float x) { return unsafe_logf_impl<DEGREE>(x); } template <int DEGREE> constexpr float approx_logf(float x) { using namespace approx_math; constexpr float MAXNUMF = 3.4028234663852885981170418348451692544e38f; //x = std::max(std::min(x,MAXNUMF),0.f); float res = unsafe_logf<DEGREE>(x); res = (x < MAXNUMF) ? res : std::numeric_limits<float>::max(); return (x > 0) ? res : std::numeric_limits<float>::quiet_NaN(); } #else template <int DEGREE> constexpr float unsafe_logf(float x) { return std::log(x); } template <int DEGREE> constexpr float approx_logf(float x) { return std::log(x); } #endif // NO_APPROX_MATH #endif