32位十进制数的浮点/双精度分析

时间:2014-05-13 10:47:27

标签: c++ c precision

从另一个人的.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的最后一个准确位数和*** {{1}的最后一个准确位数}}。

long double的输出有32位十进制数字,因为我已将double的精度设置为该值。

正如预期的那样,

std::cout输出为here

float

我希望最后一个输出与前一个输出相同,即float has 24 binary bits of precision, and double has 53. 后缀不会阻止该数字成为f。当我写这篇文章时,我

double

首先,该数字变为const double df = 0.70710678118654752440084436210485f; ,然后存储为float,因此在第24个十进制数字之后,它有零,这就是{{1精度在那里停止。

我说错了吗?

this回答我找到了一些相关信息:

double

[编辑]

关于double,它不是标准的,因此它不在竞争中。查看更多here

3 个答案:

答案 0 :(得分:5)

来自标准:

  

有三种浮点类型:float,double和long double。   double类型提供至少与float一样多的精度   类型long double提供至少与double一样多的精度。该   float类型的值集是该组值的子集   双重类型; double类型的值集是。的子集   long double类型的值集。价值表示   浮点类型是实现定义的。

所以你可以在这个问题上看到你的问题:标准实际上没有说明精确浮点数是多少。

在标准实施方面,您需要查看IEEE754,这意味着Irineau和Davidmh的另外两个答案是解决问题的完美有效方法。

至于指示类型的后缀字母,再次查看标准:

  

浮雕文字的类型是双倍的,除非明确指定   一个su ffi x。 su ffi xes f和F指定float,su ffi xes l和L指定   长双。

因此,除非您使用long double后缀,否则创建double的尝试与您分配给它的L文字具有相同的精确度。

我知道其中一些答案可能看起来不太令人满意,但在你可以解雇答案之前,有很多背景阅读要在相关标准上完成。这个答案已经比预期的要长,所以我不会试着在这里解释一切。

作为最后一点:由于精度没有明确定义,为什么没有比它需要的更长的常数?似乎总是定义一个足够精确的常量,无论其类型如何都始终可以表示。

答案 1 :(得分:1)

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位数字。但是,你真的需要它们吗?

答案 2 :(得分:1)

某些编译器具有binary128 floating point format, normalized by 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(四倍精度)。