我有一個沒有浮點支持的協處理器。我試圖使用32位固定點,但無法處理非常小的數字。我的數字範圍從1到1e-18。一種方法是使用浮點模擬,但速度太慢。如果我們知道數字不會大於1且小於1e-18,我們能否在這種情況下更快地做到這一點。或者有什麼方法可以讓定點工作在非常小的數字上。浮點仿真或給定範圍內的數字的固定點
回答
不可能對於32位定點編碼從10 -18表示爲1號這是一個事實,即從10 -18跨度爲10的比率立即明顯但是32位整數的非零編碼跨越小於2的比率,這比遠小於10的小得多。因此,沒有選擇定點編碼的比例將提供所需的範圍。
因此,32位定點編碼不起作用,您必須使用其他技術。
在某些應用中,可能適合使用多個定點編碼。也就是說,各種輸入值將用定點編碼進行編碼,但每種輸入值都具有適合其的比例尺,並且中間值和輸出也將具有定製的比例尺。顯然,只有在設計時才能確定合適的比例尺,這是可能的。否則,你應該放棄32位定點編碼並考慮其他選擇。
他*可以*使用定點對數系統,儘管在這樣的系統中增加令人討厭。 –
64位定點算法應該可以工作。根據我的經驗,LNS軟件只適用於計算添加相對較少的情況,並且準確度要求相當低,否則加減法所需的表格會變得非常大,這對於沒有FPU的低端處理器通常是個問題(即也往往有小緩存)。研究LNS的一個合理的出發點是「FOCUS微機數字系統」,CACM,1979。 – njuffa
@njuffa:目前尚不清楚64位定點算法是否可行,因爲MetallicPriest尚未回答關於所需精度的問題。 64位的跨度可以代表1e-18的大小,但它爲附加位精度留下了很小的餘地。 –
在嵌入式系統中,我建議使用16 + 32,16 + 16,8 + 16或8 + 24位冗餘浮點表示,其中每個數字只是M * 2^exp。
在這種情況下,您可以選擇用M = 0和exp = 0來表示零;對於每個2的冪,有16-32個表示 - 並且主要使得比較比典型地更難一點。也可以推遲標準化,例如減法後。
將簡化24位浮點足夠快,足夠精確?:
#include <stdio.h>
#include <limits.h>
#if UINT_MAX >= 0xFFFFFFFF
typedef unsigned myfloat;
#else
typedef unsigned long myfloat;
#endif
#define MF_EXP_BIAS 0x80
myfloat mfadd(myfloat a, myfloat b)
{
unsigned ea = a >> 16, eb = b >> 16;
if (ea > eb)
{
a &= 0xFFFF;
b = (b & 0xFFFF) >> (ea - eb);
if ((a += b) > 0xFFFF)
a >>= 1, ++ea;
return a | ((myfloat)ea << 16);
}
else if (eb > ea)
{
b &= 0xFFFF;
a = (a & 0xFFFF) >> (eb - ea);
if ((b += a) > 0xFFFF)
b >>= 1, ++eb;
return b | ((myfloat)eb << 16);
}
else
{
return (((a & 0xFFFF) + (b & 0xFFFF)) >> 1) | ((myfloat)++ea << 16);
}
}
myfloat mfmul(myfloat a, myfloat b)
{
unsigned ea = a >> 16, eb = b >> 16, e = ea + eb - MF_EXP_BIAS;
myfloat p = ((a & 0xFFFF) * (b & 0xFFFF)) >> 16;
return p | ((myfloat)e << 16);
}
myfloat double2mf(double x)
{
myfloat f;
unsigned e = MF_EXP_BIAS + 16;
if (x <= 0)
return 0;
while (x < 0x8000)
x *= 2, --e;
while (x >= 0x10000)
x /= 2, ++e;
f = x;
return f | ((myfloat)e << 16);
}
double mf2double(myfloat f)
{
double x;
unsigned e = (f >> 16) - 16;
if ((f & 0xFFFF) == 0)
return 0;
x = f & 0xFFFF;
while (e > MF_EXP_BIAS)
x *= 2, --e;
while (e < MF_EXP_BIAS)
x /= 2, ++e;
return x;
}
int main(void)
{
double testConvData[] = { 1e-18, .25, 0.3333333, .5, 1, 2, 3.141593, 1e18 };
unsigned i;
for (i = 0; i < sizeof(testConvData)/sizeof(testConvData[0]); i++)
printf("%e -> 0x%06lX -> %e\n",
testConvData[i],
(unsigned long)double2mf(testConvData[i]),
mf2double(double2mf(testConvData[i])));
printf("300 * 5 = %e\n", mf2double(mfmul(double2mf(300),double2mf(5))));
printf("500 + 3 = %e\n", mf2double(mfadd(double2mf(500),double2mf(3))));
printf("1e18 * 1e-18 = %e\n", mf2double(mfmul(double2mf(1e18),double2mf(1e-18))));
printf("1e-18 + 2e-18 = %e\n", mf2double(mfadd(double2mf(1e-18),double2mf(2e-18))));
printf("1e-16 + 1e-18 = %e\n", mf2double(mfadd(double2mf(1e-16),double2mf(1e-18))));
return 0;
}
輸出(ideone):
1.000000e-18 -> 0x459392 -> 9.999753e-19
2.500000e-01 -> 0x7F8000 -> 2.500000e-01
3.333333e-01 -> 0x7FAAAA -> 3.333282e-01
5.000000e-01 -> 0x808000 -> 5.000000e-01
1.000000e+00 -> 0x818000 -> 1.000000e+00
2.000000e+00 -> 0x828000 -> 2.000000e+00
3.141593e+00 -> 0x82C90F -> 3.141541e+00
1.000000e+18 -> 0xBCDE0B -> 9.999926e+17
300 * 5 = 1.500000e+03
500 + 3 = 5.030000e+02
1e18 * 1e-18 = 9.999390e-01
1e-18 + 2e-18 = 2.999926e-18
1e-16 + 1e-18 = 1.009985e-16
減法被留作練習。同樣更好的轉換例程。
使用64位定點並完成它。
與32位定點相比,乘法運算速度要慢四倍,但仍然比浮點仿真效率高得多。
- 1. 固定點與浮點數
- 2. 如何計算給定數字範圍內的某些點
- 3. 浮點數範圍
- 4. 浮點數的範圍
- 5. 查找給定範圍內的數字?
- 6. 從固定點到浮點數
- 7. 如何查找d3中給定範圍內的數據點數
- 8. 有效的方法來存儲一個固定範圍的浮點數
- 9. 單精度浮點數仿真
- 10. 確定給定範圍內的素數
- 11. Verilog中的固定和浮點數
- 12. 給定起始點和結束點的字母數字值的範圍
- 13. 已知範圍內的定點乘法
- 14. 漂浮點在或超出範圍?
- 15. 範圍浮點數和負的步
- 16. 找到給定範圍內的交叉點?
- 17. 給定一串數字範圍,獲得這些範圍內的所有數字?
- 18. 的Perl:固定浮點增量方向
- 19. 如何從給定的範圍中獲得給定範圍的數字R
- 20. 將浮點數轉換爲固定點的VHDL代碼?
- 21. iOS Objctive-C浮點範圍
- 22. 對數範圍(與浮點數)
- 23. 固定寬度的浮點數字格式
- 24. C++:仿真定點除法/乘法
- 25. 算法:找出給定範圍內的數字的個數
- 26. 小數點後的數字固定量
- 27. 點點參數的範圍
- 28. 在給定的範圍內比給定數目
- 29. 從sql中給定範圍的小數點
- 30. c#文本框 - 僅接受範圍內的浮點數
像往常一樣,定義您正在嘗試解決的問題。給我們你想要計算的公式。 –
計算(乘法,減法和加法)與數字,其中初始值,中間值和最終值始終在1e-18到1的範圍內,從不大於1且從不小於1e-18。 – MetallicPriest
你想要什麼精度? –