2014-01-17 77 views
6

它的嵌入式平臺,這就是爲什麼這樣的限制。四捨五入整數不使用浮動,雙或除

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有沒有辦法通過四捨五入來進一步提高精度,但不使用任何浮點,雙精度或除法。

+0

你最長的整數有多長?你對輸入「c」有最大值嗎?我想判斷你可以使用多少位。 –

+0

我將使用的最大值是120(對於C)。整數可以是32位長。 –

+0

你想要什麼精度?你所做的計算基本上把一個相當精確的定點值轉換爲一個整數,並且整數的最大可能值只是4. – 2014-01-17 17:45:55

回答

2

問題不在於精度。你正在使用很多位。

我懷疑問題是,你正在比較兩種不同的方法轉換爲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)); 
+0

順便說一句:在嵌入式環境中,16位'int'普遍存在。建議'((int32_t)1 << 24)'以防止UB並提供最大的可移植性。 (或者至少'(1L << 24)')。 – chux

+0

@chux,感謝您的建議。我編輯過它。 –

2

如何將您的常量縮放10000.然後獲得的最大數量是2035 * 120 * 120 - 24038 * 120 = 26419440,遠低於2^31的限制。所以也許沒有必要在這裏做一些真正的調整。

正如Joe Hass指出的那樣,您的問題在於您將精確位移入垃圾箱。

是否將您的小數移位2或10移到左邊並不重要。只是假裝你的小數點不在最後一位後面,而是在移位位置。如果保持計算結果,則移位2可能更容易處理。如果您只想輸出結果,請按照上述建議移動10的冪數,轉換數字並從右側插入小數點5個字符。

0

如果R輸入使用最多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; 

恆轉換將被完成在編譯時優化編譯器。

+0

我不明白這個答案@Sergey。通過改變一切,我們不會失去精確度。老實說,我不明白答案。你能多解釋一下嗎? –

+0

@UnderDog精度=您使用的位數。 「float」的精度爲23位,「雙」爲52位。通過將整數移到最大值,我們使用32位整數的全部精度。由於結果是一個整數,我們需要將結果移回正確的數量。 –

1

吉文斯:

讓我們假設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最終可能會達到一個整數,並截斷0.999999,當轉換爲int32_t

2在運行時,我們沒有進行昂貴的除法操作,而是執行>>(右移)。通過加上除數的一半(正如@Joe Hass所建議的那樣),在分組之前,我們得到了一個很好的四捨五入的答案。重要的是而不是來編碼在/這裏some_signed_int/4some_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回覆已刪除。

+0

除了在這裏增加'UD_Scaling/2'外,我沒有看到這與問題中的代碼有什麼不同。如果你看看十六進制常量,你會發現它們正是你正在計算的那些。 –

+0

@Mark Ransom好點。嗯。沒有顯示用於計算常量的OP方法。這裏是明確的 - 對未來的參考很有用。我認爲,像'0.02035'這樣的值可能會在符合條件之間改變,但是在球場上。在不知道OP的方法的情況下,如果A或B發生改變,OP的縮放常量和這些值可能會有所不同。 OP的遊標狀態爲「精度差」。如果OP總是和我一樣進行縮放,除了'UD_Scaling/2'之外,沒有更多的精度可用。答案只在整數步驟中從-71到5變化,'UD_Scaling/2'得到最好的'int32_t'答案。 – chux