簡體   English   中英

32位十進制數的浮點/雙精度分析

[英]Analysis of float/double precision in 32 decimal digits

從另一個人的.c文件中,我看到了這個:

const float c = 0.70710678118654752440084436210485f;

他想避免計算sqrt(1/2)

這可以用普通的C/C++以某種方式存儲嗎? 我的意思是沒有失去精確度。 對我來說似乎不可能。

我正在使用C ++,但我不相信這兩種語言之間的精確差異太大(如果有的話),那就是為什么我沒有測試它。

所以,我寫了幾行,看看代碼的行為:

std::cout << "Number:    0.70710678118654752440084436210485\n";

const float f = 0.70710678118654752440084436210485f;
std::cout << "float:     " << std::setprecision(32) << f << std::endl;

const double d = 0.70710678118654752440084436210485; // no f extension
std::cout << "double:    " << std::setprecision(32) << d << std::endl;

const double df = 0.70710678118654752440084436210485f;
std::cout << "doublef:   " << std::setprecision(32) << df << std::endl;

const long double ld = 0.70710678118654752440084436210485;
std::cout << "l double:  " << std::setprecision(32) << ld << std::endl;

const long double ldl = 0.70710678118654752440084436210485l; // l suffix!
std::cout << "l doublel: " << std::setprecision(32) << ldl << std::endl;

輸出是這樣的:

                   *       ** ***
                   v        v v
Number:    0.70710678118654752440084436210485    // 32 decimal digits
float:     0.707106769084930419921875            // 24 >>      >>
double:    0.70710678118654757273731092936941
doublef:   0.707106769084930419921875            // same as float
l double:  0.70710678118654757273731092936941    // same as double
l doublel: 0.70710678118654752438189403651592    // suffix l

其中*是最后的准確數字float**的最后准確的數字double***的最后准確的數字long double

double的輸出有32位十進制數,因為我已將std::cout的精度設置為該值。

float輸出有24個,如預期,如說在這里

float has 24 binary bits of precision, and double has 53.

我希望最后一個輸出與前一個輸出相同,即f后綴不會阻止數字變為double 想當我寫這篇文章時:

const double df = 0.70710678118654752440084436210485f;

會發生的是,首先數字變為float然后存儲為double ,所以在第24個十進制數字之后,它有零,這就是double精度停在那里的原因。

我對么?

這個答案我發現了一些相關的信息:

float x = 0 has an implicit typecast from int to float.
float x = 0.0f does not have such a typecast.
float x = 0.0 has an implicit typecast from double to float.

[編輯]

關於__float128 ,它不是標准的,因此它不在競爭中。 在這里查看更多。

從標准:

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

所以你可以在這個問題上看到你的問題:標准實際上並沒有說明浮點數是多么精確。

在標准實現方面,您需要查看IEEE754,這意味着Irineau和Davidmh的另外兩個答案是解決問題的完美有效方法。

至於后綴字母表示類型,再看標准:

除非由su ffi x明確指定,否則浮動文字的類型是雙重的。 su ffi xes f和F指定float,su ffi xes l和L指定long double。

因此,除非使用L后綴,否則創建long double的嘗試將與分配給它的double literal具有相同的精度。

我知道其中一些答案可能看起來不太令人滿意,但在您解決答案之前,有相關標准需要進行大量的背景閱讀。 這個答案已經比預期的要長,所以我不會試着在這里解釋一切。

最后要注意的是:由於精度沒有明確定義,為什么不能獲得比它需要的更長的常數? 似乎總是定義一個足夠精確的常量,以便始終可以表示,而不管類型如何。

Python的數值庫numpy具有非常方便的浮點信息功能。 所有類型都相當於C:

對於C的浮點數:

print numpy.finfo(numpy.float32)
Machine parameters for float32
---------------------------------------------------------------------
precision=  6   resolution= 1.0000000e-06
machep=   -23   eps=        1.1920929e-07
negep =   -24   epsneg=     5.9604645e-08
minexp=  -126   tiny=       1.1754944e-38
maxexp=   128   max=        3.4028235e+38
nexp  =     8   min=        -max
---------------------------------------------------------------------

對於C的雙倍:

print numpy.finfo(numpy.float64)
Machine parameters for float64
---------------------------------------------------------------------
precision= 15   resolution= 1.0000000000000001e-15
machep=   -52   eps=        2.2204460492503131e-16
negep =   -53   epsneg=     1.1102230246251565e-16
minexp= -1022   tiny=       2.2250738585072014e-308
maxexp=  1024   max=        1.7976931348623157e+308
nexp  =    11   min=        -max
---------------------------------------------------------------------

對於C的長期浮動:

print numpy.finfo(numpy.float128)
Machine parameters for float128
---------------------------------------------------------------------
precision= 18   resolution= 1e-18
machep=   -63   eps=        1.08420217249e-19
negep =   -64   epsneg=     5.42101086243e-20
minexp=-16382   tiny=       3.36210314311e-4932
maxexp= 16384   max=        1.18973149536e+4932
nexp  =    15   min=        -max
---------------------------------------------------------------------

所以,即使長浮點數(128位)也不會給你想要的32位數。 但是,你真的需要它們嗎?

一些編譯器具有binary128浮點格式的實現,由IEEE 754-2008標准化 例如,使用gcc,類型為__float128 該浮點格式具有大約34個十進制精度( log(2^113)/log(10) )。

您可以使用Boost Multiprecision庫來使用它們的包裝器float128 該實現將使用本機類型(如果可用),或使用直接替換。

讓我們用新的非標准類型__float128擴展您的實驗,使用最近的g ++(4.8):

// Compiled with g++ -Wall -lquadmath essai.cpp
#include <iostream>
#include <iomanip>
#include <quadmath.h>
#include <sstream>

std::ostream& operator<<(std::ostream& out, __float128 f) {
  char buf[200];
  std::ostringstream format;
  format << "%." << (std::min)(190L, out.precision()) << "Qf";
  quadmath_snprintf(buf, 200, format.str().c_str(), f);
  out << buf;
  return out;
}

int main() {
  std::cout.precision(32);
  std::cout << "Number:    0.70710678118654752440084436210485\n";

  const float f = 0.70710678118654752440084436210485f;
  std::cout << "float:     " << std::setprecision(32) << f << std::endl;

  const double d = 0.70710678118654752440084436210485; // no f extension
  std::cout << "double:    " << std::setprecision(32) << d << std::endl;

  const double df = 0.70710678118654752440084436210485f;
  std::cout << "doublef:   " << std::setprecision(32) << df << std::endl;

  const long double ld = 0.70710678118654752440084436210485;
  std::cout << "l double:  " << std::setprecision(32) << ld << std::endl;

  const long double ldl = 0.70710678118654752440084436210485l; // l suffix!
  std::cout << "l doublel: " << std::setprecision(32) << ldl << std::endl;

  const __float128 f128 = 0.70710678118654752440084436210485;
  const __float128 f128f = 0.70710678118654752440084436210485f; // f suffix
  const __float128 f128l = 0.70710678118654752440084436210485l; // l suffix
  const __float128 f128q = 0.70710678118654752440084436210485q; // q suffix

  std::cout << "f128:      " << f128 << std::endl;
  std::cout << "f f128:    " << f128f << std::endl;
  std::cout << "l f128:    " << f128l << std::endl;
  std::cout << "q f128:    " << f128q << std::endl;
}

輸出是:

                   *       ** ***        ****
                   v        v v             v
Number:    0.70710678118654752440084436210485
float:     0.707106769084930419921875
double:    0.70710678118654757273731092936941
doublef:   0.707106769084930419921875
l double:  0.70710678118654757273731092936941
l doublel: 0.70710678118654752438189403651592
f128:      0.70710678118654757273731092936941
f f128:    0.70710676908493041992187500000000
l f128:    0.70710678118654752438189403651592
q f128:    0.70710678118654752440084436210485

其中*float的最后一個准確數字, ** double的最后一個准確數字, *** long double的最后一個准確數字, ****__float128的最后一個准確數字。

正如另一個答案所說,C ++標准沒有說明各種浮點類型的精度是什么(就像它沒有說明整數類型的大小一樣)。 它僅指定這些類型的最小精度/大小。 但規范IEEE754確實指明了所有這些! 所有很多架構的FPU確實實現該規范IEEE745,以及最近的gcc版本實現類型binary128與擴展規范的__float128

至於你的代碼或我的代碼的解釋,像0.70710678118654752440084436210485f這樣的表達式是一個浮點字面值。 它有一個類型,由后綴定義,這里ffloat 因此,文字的值對應於給定數字中給定類型的最近值。 這解釋了為什么,例如,“doublef”的精度與代碼中的“float”的精度相同。 在最近的gcc版本中,有一個擴展,允許定義類型為__float128浮點文字,帶有Q后綴(四倍精度)。

暫無
暫無

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

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