在C++中,float占4個字節,double占8個字節,均采用 IEEE 754 浮點標准;內部都是以二進制為基礎,表述實數,有些實數可以被精確表述,比如0.2,但有些不行,比如0.3。針對這一點,前不久有篇專門的文章介紹這個:浮點運算為什么不准?有人為0.30000000000000004建了個網站
IEEE 754浮點標准
IEEE標准包含一組實數的二進制表示。一個浮點數字包含三個部分:符號(+或者-)、尾數(包含一串有效數位)和一個指數,這些部分都在一個計算機字里。常用的浮點數精度有:單精度(C++內的float類型)、雙精度(C++內的double類型),分配的數位分別是32、64。這些數位被分成如下不同的部分:
| 精度 | 符號 | 指數 | 尾數 |
|---|---|---|---|
| float | 1 | 8 | 23 |
| double | 1 | 11 | 52 |
以雙精度浮點數為例,每個浮點數字被分配了8字節,或者64位,來存儲對應的三個部分。每個字都具有如下的形式
其中第一位保存了符號位,后面11位用於保存指數,再后面小數點后的52位保存尾數。符號位是0表示正數,1表示負數。11位的指數表示的正二進制整數,這些正數通過往指數上疊加 \(2^{10}-1=1023\) 得到,指數范圍在-1022和1023之間。e1...e11覆蓋了從1到2046之間對應的指數,由於特殊目的,這里沒有使用0和2047。
機器精度
機器精度對應的數字,是1和比1大的最小浮點數之間的距離,對於IEEE雙精度浮點表示,該值為 \(2^{-52}\),對應 float.h 文件內宏 DBL_EPSILON 的值。
最小的可表達雙精度數字
\(2^{-52}*2^{-1022}\) 是最小的可表達雙精度數字。最小可表達數字和機器精度之間的差別是在於,許多在機器精度以下的數字都是機器可表達的數字,盡管將這些數字加到1上可能沒有什么影響,另一方面,在最小可表達數字以下的雙精度數字完全不能被表示。
boost multiprecision
為了能夠表述更高精度的浮點數,就得向庫方向查找了。gmp, mpfr 可以表述無限精度,但編譯只能gcc吧,boost在1.56版時開始提供 boost.multiprecision 用於支持更高精度數值表述,許可證較其他庫寬松,但在計算效率等方向要遜於gmp, mpfr。
比如采用 bbp 公式(可參考The BBP Algorithm for Pi)計算 pi 的精確結果,代碼如下:
// Author: bitbybit3d@163.com
#include <iostream>
#include <iomanip>
#include <boost/math/constants/constants.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
// 通過 BBP 公式計算 PI
template <class Type>
Type calc_pi_bbp(int n)
{
Type pi = 0;
std::streamsize prevsize = std::cout.precision(50);
for (int k = 0; k < n; ++k)
{
Type it = static_cast<Type>(1) / pow(16, k)
* (static_cast<Type>(4) / (8 * k + 1)
- static_cast<Type>(2) / (8 * k + 4)
- static_cast<Type>(1) / (8 * k + 5)
- static_cast<Type>(1) / (8 * k + 6));
pi += it;
std::cout << std::left << std::setw(8) << k << pi << std::endl;
}
std::cout.precision(prevsize);
return pi;
}
int main(int argc, char* argv[])
{
std::cout << "Use double: " << std::endl;
calc_pi_bbp<double>(20);
std::cout << std::endl;
std::cout << "Use boost multiprecision: " << std::endl;
calc_pi_bbp<boost::multiprecision::cpp_bin_float_100>(30);
return 0;
}
以double值計算20次結果如下:

再以boost.multiprecision計算30次結果如下:

明顯使用double計算時,第10次之后就每沒有什么變化了(小於2.2204460492503131e-016 (即DBL_EPSILON)的值與 1.0 相加仍然為1.0),而boost.multiprecision還一直在變化,並且結果可以與Window操作系統中計算器存儲的pi值相同(但小數點之后更多數字,這些准不准確就沒比較了)。
