簡體   English   中英

C++ 中浮點數據類型的精度

[英]Precision of floating-point data types in C++

為什么浮點數據類型的精度不與其大小成正比? 例如:

std::cout << sizeof(float) << "\n";  // this gives 4 on my machine "debian 64 bit" with "gcc 6.3.0"  
std::cout << std::numeric_limits<float>::digits10  << "\n"; // gives 6

std::cout << sizeof(double) << "\n";  // gives 8
std::cout << std::numeric_limits<double>::digits10 <<  "\n"; // gives 15

std::cout << sizeof(long double) <<  "\n";  // gives 16
std::cout << std::numeric_limits<long double>::digits10  << "\n"; // gives 18

如您所見, double的精度大約是float精度的兩倍,這是有道理的,因為double的大小是float大小的兩倍。

但是doublelong double就不一樣了, long double double的大小是128位,是64位double的兩倍,但是它的精度只多了三位數!!

我不知道浮點數是如何實現的,但從理性的角度來看,為了三位數的精度而多使用 64 位內存是否有意義?!

我四處搜索,但沒能找到一個簡單直接的答案。 如果有人能解釋為什么long double的精度只比double多三位數,你能解釋一下為什么這與doublefloat之間的情況不同嗎?

而且我還想知道如何在不定義我自己的數據類型的情況下獲得更好的精度,這顯然會以犧牲性能為代價?

“精度”並不是浮點值的全部。 它還與“幅度”有關(雖然不確定該術語是否正確:)? 表示值可以變成多大(或小)?

為此,嘗試打印每種類型的max_exponent

std::cout << "float: " << sizeof(float) << "\n";
std::cout << std::numeric_limits<float>::digits << "\n";
std::cout << std::numeric_limits<float>::max_exponent << "\n";

std::cout << "double: " << sizeof(double) << "\n";
std::cout << std::numeric_limits<double>::digits << "\n";
std::cout << std::numeric_limits<double>::max_exponent << "\n";

std::cout << "long double: " <<  sizeof(long double) << "\n";
std::cout << std::numeric_limits<long double>::digits << "\n";
std::cout << std::numeric_limits<long double>::max_exponent << "\n";

ideone上的輸出:

float: 4
24
128
double: 8
53
1024
long double: 16
64
16384

所以額外的位並不是全部用於表示更多的數字(精度)而是允許指數更大。 使用IEE 754 long double中的措辭主要增加指數范圍而不是精度。

我上面的 ideone 示例顯示的格式(可能)顯示“x86 擴展精度格式” ,它為整數部分分配 1 位,為小數部分分配 63 位(總共 64 位)和 15 位(2^(15- 1) = 16384, 1 位用於指數的符號) 為指數。

請注意,C++ 標准只要求long double至少與double一樣精確,因此long double可以是double的同義詞,顯示的 x86 擴展精度格式(最有可能)或更好(AFAIK 僅 PowerPC 上的 GCC)。

而且我還想知道如何在不定義我自己的數據類型的情況下獲得更好的精度,這顯然會以犧牲性能為代價?

您需要自己編寫(當然是學習經驗,最好不要為生產代碼編寫)或使用庫,例如 GNU MPFRBoost.Multiprecision

C++ 標准沒有為浮點類型設置固定要求,除了它們必須滿足的一些最低級別。

您正在使用的 C++ 實現可能針對 Intel 處理器。 除了常見的 IEEE-754 基本 32 位和 64 位二進制浮點格式外,Intel 還有一種 80 位格式。 您的 C++ 實現可能將其用於long double

Intel 的 80 位格式比 64 位double精度格式多 11 位作為有效數字。 (它實際上使用 64,而double格式使用 52,但其中一個保留用於顯式前導 1。)多 11 位意味着 2 11 = 2048 倍有效數值,大約多了三位十進制數字。

80 位格式(十字節)優先對齊到 16 字節的倍數,因此包含六個字節的填充以使long double大小成為 16 字節的倍數。

您的問題中有很多不正確的假設

首先,在 C++ 中沒有關於類型大小的要求。 該標准只規定了每種類型的最低精度,並且......

... double類型至少提供與float一樣多的精度,而long double類型至少提供與double一樣多的精度。 float類型的值集是double類型值集的子集; double類型的值集是long double類型值集的子集。 浮點類型的值表示是實現定義的。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

大多數現代實現將floatdouble映射到IEEE-754 單精度和雙精度格式,因為對它們的硬件支持是主流。 然而long double沒有如此廣泛的支持,因為很少有人需要比 double 更高的精度,而且這些硬件的成本要高得多。 因此,一些平台將其映射為 IEEE-754 雙精度,即與double相同。 如果底層硬件支持,其他一些將其映射到80 位 IEEE 754 擴展精度格式 否則long double將由double-double算術IEEE-754 四倍精度表示

此外,精度也不會與類型中的位數成線性比例關系 很容易看出double的精度是float的兩倍多范圍是 float 的 8 倍float盡管存儲空間只有兩倍,因為它有 53 位有效位,而 float 有 24 位,指數位多 3 位。 類型也可以有陷阱表示或填充位,因此不同的類型可能有不同的范圍,即使它們具有相同的大小並且屬於相同的類別(整數或浮點數)

所以這里重要的是std::numeric_limits<long double>::digits 如果您打印它,您會看到long double有 64 位有效位,僅比double多 11 位。 現場觀看 這意味着您的編譯器對long double使用 80 位擴展精度,其余只是填充字節以保持對齊。 事實上gcc 有多種選項可以改變你的輸出:

  • -malign-double-mno-align-double用於控制long double的對齊方式
  • -m96bit-long-double-m128bit-long-double用於更改填充大小
  • -mlong-double-64-mlong-double-80-mlong-double-128用於控制底層long double實現

通過更改選項,您將獲得以下long double結果

如果您禁用填充,您將獲得 size = 10,但由於未對齊,這將以性能為代價。 編譯器資源管理器上查看更多演示

在 PowerPC 中,您也可以在更改浮點格式時看到相同的現象 使用-mabi=ibmlongdouble (double-double 算術,這是默認值),您將得到 (size, digits10, digits2) = (16, 31, 106) 但使用-mabi=ieeelongdouble時,元組將變為 (16, 33, 113)

有關更多信息,您應該閱讀https://en.wikipedia.org/wiki/Long_double

而且我還想知道如何在不定義自己的數據類型的情況下獲得更高的精度

要搜索的關鍵字是arbitrary-precision arithmetic 您可以在List of arbitrary-precision arithmetic software中找到各種庫。 您可以在標簽中找到更多信息

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM