浮點運算與boost.multiprecision


在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位,來存儲對應的三個部分。每個字都具有如下的形式

\[se_1e_2...e_{11}b_1b_2...b_{52} \]

其中第一位保存了符號位,后面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次結果如下:
image

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM