我需要在C中表示非常小的指令(例如,0.6745 * 2^{ - 3000})的浮點數。有必要支持這種支持與平臺無關(工作在CPU和GPU-CUDA上)。有效數字的大長度不是必需的。 我無法使用高精度庫(GMP,MPFR等),因爲它們不能在GPU上工作。另一方面,CUDA不支持long double類型。有沒有解決方法?是否有可能以某種方式實現自定義浮點類型?具有擴展範圍的浮點類型
回答
我寫的(使用this工作)簡單的解決方案:
#include <math.h>
#include <stdint.h>
#define DOUBLE_PRECISION 53
/* DOUBLE PRECISION FLOATING-POINT TYPE WITH EXTENDED EXPONENT */
typedef struct Real {
double sig; //significand
long exp; //binary exponent
} real;
/* UNION FOR DIVISION DOUBLE BY 2^POW */
union DubleIntUnion
{
double dvalue;
uint64_t ivalue;
};
/* PLACE SIGNIFICAND OF REAL NUMBER IN RANGE [1, 2) */
inline real adjust(real x){
real y;
y.exp = x.exp;
y.sig = x.sig;
if(y.sig == 0){
y.exp = 0;
} else if (fabs(y.sig) >= 2.0){
y.exp = y.exp + 1;
y.sig = y.sig/2;
} else if(fabs(y.sig) < 1){
y.exp = y.exp - 1;
y.sig = y.sig * 2;
}
return y;
}
/* PLACE SIGNIFICAND OF REAL NUMBER IN RANGE [1, 2) FOR TINY NUMBER */
/* FOR EXAMPLE, AFTER SUBTRATION OR WHEN SET REAL FROM DOUBLE */
inline real adjusttiny(real x){
real y;
y.exp = x.exp;
y.sig = x.sig;
while(1){
x.exp = y.exp;
x.sig = y.sig;
y = adjust(x);
if(x.exp == y.exp && x.sig == y.sig)
break;
}
return y;
}
real set(double x){
real y;
real z;
y.sig = x;
y.exp = 0;
return adjusttiny(y);
};
real set(real x){
real y;
y.exp = x.exp;
y.sig = x.sig;
return y;
};
/* ARITHMETIC OPERATIONS */
//divide x by 2^pow. Assert that x.exp - pow > e_min
inline double div2pow(const double x, const int pow)
{
DubleIntUnion diu;
diu.dvalue = x;
diu.ivalue -= (uint64_t)pow << 52; // subtract pow from exponent
return diu.dvalue;
}
//summation
inline real sum(real x, real y){
real sum;
int dexp = abs(x.exp - y.exp);
if (x.exp > y.exp){
sum.exp = x.exp;
if(dexp <= DOUBLE_PRECISION){
sum.sig = div2pow(y.sig, dexp); // divide y by 2^(x.exp - y.exp)
sum.sig = sum.sig + x.sig;
} else sum.sig = x.sig;
} else if (y.exp > x.exp){
sum.exp = y.exp;
if(dexp <= DOUBLE_PRECISION){
sum.sig = div2pow(x.sig, dexp); // divide x by 2^(y.exp - x.exp)
sum.sig = sum.sig + y.sig;
} else
sum.sig = y.sig;
} else {
sum.exp = x.exp;
sum.sig = x.sig + y.sig;
}
return adjust(sum);
}
//subtraction
inline real sub(real x, real y){
real sub;
int dexp = abs(x.exp - y.exp);
if (x.exp > y.exp){
sub.exp = x.exp;
if(dexp <= DOUBLE_PRECISION){
sub.sig = div2pow(y.sig, dexp); // divide y by 2^(x.exp - y.exp)
sub.sig = x.sig - sub.sig;
} else sub.sig = x.sig;
} else if (y.exp > x.exp){
sub.exp = y.exp;
if(dexp <= DOUBLE_PRECISION){
sub.sig = div2pow(x.sig, dexp); // divide x by 2^(y.exp - x.exp)
sub.sig = sub.sig - y.sig;
} else sub.sig = -y.sig;
} else {
sub.exp = x.exp;
sub.sig = x.sig - y.sig;
}
return adjusttiny(sub);
}
//multiplication
inline real mul(real x, real y){
real product;
product.exp = x.exp + y.exp;
product.sig = x.sig * y.sig;
return adjust(product);
}
//division
inline real div(real x, real y){
real quotient;
quotient.exp = x.exp - y.exp;
quotient.sig = x.sig/y.sig;
return adjust(quotient);
}
乍一看工作正常。也許我錯過了一些東西,或者實施可以加速?
如何在這些數字上實現功能floor
和ceil
?
因爲這個代碼是C++ ,'abs()'是重載的,因此它可以像C++一樣工作。但是帖子被標記爲'C'並且'abs()'適用於整數,所以這段代碼會使數學部分失敗。建議使用該帖子的編程語言發佈答案。 – chux 2015-03-19 14:55:12
在數學方面,1)IMO沒有看到使用範圍'(1/2,2)'而不是單個二進制範圍'[1,2]'的值。使用一個固定的範圍(當'exp> LONG_MIN'時)可以簡化各種操作。 2)像'1 << x.exp - y.exp'這樣的代碼存在很多問題,如果差值超過'long'位寬度,則爲UB; 3)代碼如'sum.sig = sum.sig + y。 sig;'不檢測溢出。 – chux 2015-03-19 15:07:38
@chux謝謝。如果你存儲一個單獨的指數,我會盡量考慮你的意見,並更正實施 – 2015-03-20 08:01:53
你可以在日誌空間工作,即代表每個號碼作爲電子郵件X其中x是標準的浮點類型:
加法/減法(和求和更普遍)可以執行使用log-sum-exp trick,即
- ËX + E ŷ = E X(1 + E YX)= E X +日誌(1 + EXP(YX))
乘法/除法成爲加法/減法
- ËX ×ëX = E x + y
擡高到po WER是相當簡單的
- (E X)^(E X)= E X EXP(Y)
如果你需要非常大的指數則symmetric level-index arithmetic可能滿足您的需求。然而,準確度難以預測,因此您可能需要更高的LI值來進行補償。提高精確度的一種常見方式是在CUDA上也使用很多的double-double arithmetic。
也有許多多倍庫的CUDA像CUMP
一些更多的信息:
https://devtalk.nvidia.com/default/topic/512234/gpump-multiple-precision-arithmetic-on-the-gpu-/
http://individual.utoronto.ca/haojunliu/courses/ECE1724_Report.pdf
http://gcl.cis.udel.edu/publications/meetings/090709_ARLvisit/090709_Library4GPU.pdf
http://www.cra.org/Activities/craw_archive/dmp/awards/2009/Padron/proposal_omar_dreu_09.pdf
- 1. 是否可以創建具有最小和最大範圍的浮點類型?
- 2. 浮點數範圍
- 3. Python中的擴展範圍
- 4. 浮點數的範圍
- 5. Swift 3特定類型的擴展範圍
- 6. C++標準在哪裏定義浮點類型的值範圍?
- 7. Java - 具體類擴展的泛型類
- 8. 如何擴展此範圍
- 9. Symfony2:範圍擴展異常
- 10. 擴展javascript功能範圍
- 11. 擴展指令範圍AngularJS
- 12. 在Angular.js中擴展$範圍
- 13. Asp.Net:擴展範圍驗證
- 14. 在VBA中擴展範圍
- 15. 我如何通過一個有點關聯的模型進行範圍擴展?
- 16. iOS Objctive-C浮點範圍
- 17. MySQL浮點類型範圍和精度混淆
- 18. 擴展擴展類型
- 19. 具有特定類型的泛型列表的擴展?
- 20. 如何擴展validates_uniqueness_of的範圍,超出給定模型的列範圍?
- 21. 類類型範圍
- 22. 浮點數/雙精度浮點數的小範圍,適用於整型(C++,VS2010)
- 23. 具有「擴展」的Java泛型
- 24. Javascript的範圍,安全性和擴展
- 25. 範圍浮點數和負的步
- 26. 浮點值的範圍分析?
- 27. F# - 類型擴充VS類型擴展
- 28. 擴展Haskell的類型類
- 29. 的Java擴展類類型
- 30. 帶浮點數組的Swift 3擴展
你舉的例子值是一個'雙的範圍內'。 – 2015-03-19 08:23:32
@OliverCharlesworth謝謝。我糾正了例子。 – 2015-03-19 09:07:03
也許高精度庫可以適應GPU? IIRC他們都有他們的算法的後備純C實現。 [CUMP](http://www.hpcs.cs.tsukuba.ac.jp/~nakayama/cump/)似乎是這樣一個項目。 – user4815162342 2015-03-19 09:12:59