[英]Rounding up integer without using float, double, or division
它的嵌入式平台就是為什么會出現這種限制。
original equation: 0.02035*c*c - 2.4038*c
做過這個:
int32_t val = 112; // this value is arbitrary
int32_t result = (val*((val * 0x535A8) - 0x2675F70));
result = result>>24;
精度仍然很差。 當我們乘以val*0x535A8
有沒有辦法我們可以通過向上舍入來進一步提高精度,但不使用任何浮點數,雙精度或除法。
如何將常數縮放10000.你得到的最大數字是2035 * 120 * 120 - 24038 * 120 = 26419440,遠低於2 ^ 31的限制。 所以也許沒有必要在這里進行真正的比特調整。
如Joe Hass所述,您的問題是您將精確位移到垃圾箱中。
無論是將小數點數移動2還是向左移動10都不重要。 只是假裝你的小數點不在最后一位后面,而是在移位位置。 如果繼續使用結果進行計算,則移位2可能更容易處理。 如果您只想輸出結果,請按上述建議移動十次冪,轉換數字並從右側插入小數點5個字符。
問題不是精確。 你使用了很多比特。
我懷疑問題是你正在比較轉換為int
兩種不同方法。 第一個是double
演員,第二個是右移的截斷。
將浮點數轉換為整數只會丟棄小數部分,從而導致向零舍入 ; 右移是圓形或地板。 對於正數,沒有區別,但對於負數,這兩種方法相互之間會相差1。 請參閱http://ideone.com/rkckuy上的示例和Wikipedia上的一些背景閱讀。
您的原始代碼很容易修復:
int32_t result = (val*((val * 0x535A8) - 0x2675F70));
if (result < 0)
result += 0xffffff;
result = result>>24;
請訪問http://ideone.com/D0pNPF查看結果
您也可能只是決定正確的移位結果是否正常。 轉換錯誤不大於其他方法的轉換錯誤,只是不同。
編輯:如果你想進行舍入而不是截斷,答案就更容易了。
int32_t result = (val*((val * 0x535A8) - 0x2675F70));
result = (result + (1L << 23)) >> 24;
我將與其他一些人一起建議你使用一個常量表達式來代替那些魔術常量來記錄它們的派生方式。
static const int32_t a = (int32_t)(0.02035 * (1L << 24) + 0.5);
static const int32_t b = (int32_t)(2.4038 * (1L << 24) + 0.5);
int32_t result = (val*((val * a) - b));
吉文斯:
假設1 <= c <= 120,
原方程:0.02035 * c * c - 2.4038 * c
然后-70.98586 <f(c)<4.585
- > -71 <= result <= 5
將f(c)舍入到最近的int32_t
。
參數A = 0.02035,B = 2.4038
A&B可能會隨后編譯而改變,但不會在運行時更改。
允許編碼器輸入0.02035和2.4038之類的值。 這里顯示的關鍵組件和其他人用它來縮放因子如0.02035到2的冪,得到方程式(簡化為形式(A * c-B)* c)並將結果縮放。
重要特征:
1確定A和B時,確保編譯時浮點乘法和最終轉換通過循環而不是截斷進行。 使用正值, + 0.5
實現。 沒有一個舍入的答案UD_A*UD_Scaling
可能會在一個整數下結束並在轉換為int32_t
時截斷0.999999
2而不是在運行時進行昂貴的划分,我們做>>(右移)。 通過添加一半除數(由@Joe Hass建議),在除法之前,我們得到一個非常圓潤的答案。 重要的是不要在/
here中編碼,因為some_signed_int / 4
和some_signed_int >> 2
不會以相同的方式進行。 使用2的補碼, >>
截斷朝向INT_MIN
而/
截斷為0。
#define UD_A (0.02035)
#define UD_B (2.4038)
#define UD_Shift (24)
#define UD_Scaling ((int32_t) 1 << UD_Shift)
#define UD_ScA ((int32_t) (UD_A*UD_Scaling + 0.5))
#define UD_ScB ((int32_t) (UD_B*UD_Scaling + 0.5))
for (int32_t val = 1; val <= 120; val++) {
int32_t result = ((UD_A*val - UD_B)*val + UD_Scaling/2) >> UD_Shift;
printf("%" PRId32 "%" PRId32 "\n", val, result);
}
示例差異:
val, OP equation, OP code, This code
1, -2.38345, -3, -2
54, -70.46460, -71, -70
120, 4.58400, 4, 5
這是一個新的答案。 我的舊+1回答已刪除。
如果您的輸入使用最多7位並且您有32位可用,那么您最好的選擇是將所有位移到盡可能多的位並使用它:
int32_t result;
result = (val * (int32_t)(0.02035 * 0x1000000)) - (int32_t)(2.4038 * 0x1000000);
result >>= 8; // make room for another 7 bit multiplication
result *= val;
result >>= 16;
在編譯時,優化編譯器將進行常量轉換。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.