[英]C++ POW(X,Y) X negative double and Y negative double, gives nan as result
[英]Calculating in an optimized way pow(x,y) for 0<=x<=1 and 1<y<2 in C++
對於我的一個項目,我需要非常快速地計算pow(x,y)
。 希望它被限制在一個精確的域中,但是如果它不夠快的話,它也需要足夠的內存效率。
就像我說的那樣,它在x的短范圍內介於0和1之間,而y在1和2之間。因此,它在整個范圍上必須足夠精確,以使其在遞歸調用時甚至略有減小(並且不會停頓)在一個數字上)
如果你們遇到這樣的事情或有建議...
將pow(x,y)
替換為
exp(y*log(x))
有時也是C庫的實現,但是對於許多整數輸入,其結果會有些許失真。 因此, pow(x,y)
實現通常會產生開銷,以捕捉具有指數0和1,整數冪的瑣碎冪,並進行一些其他轉換以獲得最精確的結果。 減少這些開銷可能已經足夠提高速度。
在x
和p
的這種有界區間上,最佳方法是運行exp2()
和log2()
兩個優化近似值。 由於來自exp2()
極小極大多項式的誤差將按指數方式復合,但對於0 < x ≤ 1
,輸入始終為p*log2(x) ≤ 0
,因此誤差表現良好。 注意:在x = 0, log2(0) = -∞
。 下面是一個示例代碼powf()
例程和兩個多力量的誤差圖1 ≤ p ≤ 2
和多個訂單極小極大近似兩個log2()
和exp2
有希望滿足您的絕對誤差容限。
float aPowf(float x, float p){
union { float f; uint32_t u; } L2x, e2e;
float pL2, pL2r, pL2i, E2;
if(x == 0) return(0.0f);
/* Calculate log2(x) Approximation */
L2x.f = x;
pL2 = (uint8_t)(L2x.u >> 23) - 127; // log2(m*2^e) = log2(m) + e
L2x.u = (L2x.u & ~(0xFF << 23)) | (0x7F << 23);
// Approximate log2(x) over 1 <= x < 2, use fma() fused multiply accumulate function for efficient evaluation
// pL2 += -0xf.4fb9dp-4 + L2x.f; // 4.303568e-2
// pL2 += -0x1.acc4cap0 + L2x.f * (0x2.065084p0 + L2x.f * (-0x5.847fe8p-4)); // 4.9397e-3
// pL2 += -0x2.2753ccp0 + L2x.f * (0x3.0c426p0 + L2x.f * (-0x1.0d47dap0 + L2x.f * 0x2.88306cp-4)); // 6.3717e-4
pL2 += -0x2.834a9p0 + L2x.f * (0x4.11f1d8p0 + L2x.f * (-0x2.1ee4fcp0 + L2x.f * (0xa.52841p-4 + L2x.f * (-0x1.4e4cf6p-4)))); // 8.761e-5
// pL2 += -0x2.cce408p0 + L2x.f * (0x5.177808p0 + L2x.f * (-0x3.8cfd5cp0 + L2x.f * (0x1.a19084p0 + L2x.f * (-0x6.aa30dp-4 + L2x.f * 0xb.7cb75p-8)))); // 1.2542058e-5
// pL2 += -0x3.0a3514p0 + L2x.f * (0x6.1cbb88p0 + L2x.f * (-0x5.5737a8p0 + L2x.f * (0x3.490a04p0 + L2x.f * (-0x1.442ae8p0 + L2x.f * (0x4.66497p-4 + L2x.f * (-0x6.925fe8p-8)))))); // 1.8533e-6
// pL2 += -0x3.3eb71cp0 + L2x.f * (0x7.2194ep0 + L2x.f * (-0x7.7cf968p0 + L2x.f * (0x5.c642f8p0 + L2x.f * (-0x2.faeb44p0 + L2x.f * (0xf.9e012p-4 + L2x.f * (-0x2.ef86f8p-4 + L2x.f * 0x3.dc524p-8)))))); // 2.831e-7
// pL2 += -0x3.6c382p0 + L2x.f * (0x8.23b47p0 + L2x.f * (-0x9.f803dp0 + L2x.f * (0x9.3b4f3p0 + L2x.f * (-0x5.f739ep0 + L2x.f * (0x2.9cb704p0 + L2x.f * (-0xb.d395dp-4 + L2x.f * (0x1.f3e2p-4 + L2x.f * (-0x2.49964p-8)))))))); // 4.7028674e-8
pL2 *= p;
// if(pL2 <= -128) return(0.0f);
/* Calculate exp2(p*log2(x)) */
pL2i = floorf(pL2);
pL2r = pL2 - pL2i;
e2e.u = ((int)pL2i + 127) << 23;
// Approximate exp2(x) over 0 <= x < 1, use fma() fused multiply accumulate function for efficient evaluation.
// E2 = 0xf.4fb9dp-4 + pL2r; // 4.303568e-2
// E2 = 0x1.00a246p0 + pL2r * (0xa.6aafdp-4 + pL2r * 0x5.81078p-4); // 2.4761e-3
// E2 = 0xf.ff8fcp-4 + pL2r * (0xb.24b0ap-4 + pL2r * (0x3.96e39cp-4 + pL2r * 0x1.446bc8p-4)); // 1.0705e-4
E2 = 0x1.00003ep0 + pL2r * (0xb.1663cp-4 + pL2r * (0x3.ddbffp-4 + pL2r * (0xd.3b9afp-8 + pL2r * 0x3.81ade8p-8))); // 3.706393e-6
// E2 = 0xf.ffffep-4 + pL2r * (0xb.1729bp-4 + pL2r * (0x3.d79b5cp-4 + pL2r * (0xe.4d721p-8 + pL2r * (0x2.49e21p-8 + pL2r * 0x7.c5b598p-12)))); // 1.192e-7
// E2 = 0x1.p0 + pL2r * (0xb.17215p-4 + pL2r * (0x3.d7fb5p-4 + pL2r * (0xe.34192p-8 + pL2r * (0x2.7a7828p-8 + pL2r * (0x5.15bd08p-12 + pL2r * 0xe.48db2p-16))))); // 2.9105833e-9
// E2 = 0x1.p0 + pL2r * (0xb.17218p-4 + pL2r * (0x3.d7f7acp-4 + pL2r * (0xe.35916p-8 + pL2r * (0x2.761acp-8 + pL2r * (0x5.7e9f9p-12 + pL2r * (0x9.70c6ap-16 + pL2r * 0x1.666008p-16)))))); // 8.10693e-11
// E2 = 0x1.p0 + pL2r * (0xb.17218p-4 + pL2r * (0x3.d7f7b8p-4 + pL2r * (0xe.35874p-8 + pL2r * (0x2.764dccp-8 + pL2r * (0x5.76b95p-12 + pL2r * (0xa.15ca6p-16 + pL2r * (0xf.94e0dp-20 + pL2r * 0x1.cc690cp-20))))))); // 3.9714478e-11
return(E2 * e2e.f);
}
一旦選擇了合適的極小極大值近似值,請確保使用融合的乘累加運算fma()[這是單周期指令]來實現Horner多項式求值。
可以通過引入LUT來實現某種程度的距離減小,以提高對數函數的精度,從而進一步提高精度。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.