C ++中浮点数据类型的精度

问题描述 投票:2回答:3

为什么浮点数据类型的精度与其大小成正比?例如。:

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的大小是128位,是64位double的两倍,但它的精度只有三位数!

我不知道浮点数是如何实现的,但从理性的角度来看,使用64位以上的内存只有三位精度才有意义?!

我四处搜索,但无法找到一个简单明了的答案。如果有人可以解释为什么long double的精度只比double高三位数,你能解释为什么这与doublefloat之间的情况不一样吗?

而且我还想知道如何在不定义我自己的数据类型的情况下获得更好的精度,这显然会牺牲性能?

c++ gcc floating-point precision long-double
3个回答
2
投票

“精度”并不是浮点值的全部。它也是关于“量级”(不确定该术语是否正确!):所表示的值有多大(或小)?

为此,尝试打印每种类型的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示例显示的格式(可能)显示为整数部分分配1位的"x86 extended precision format",分数部分(共64位)和15位(2 ^(15-1)= 16384)分配63位,指数的1位用于指数的符号)。

请注意,C ++标准只要求long double至少与double一样精确,因此long double可以是double的同义词,显示的x86扩展精度格式(最有可能)或更好(在PowerPC上仅AFAIK GCC)。

而且我还想知道如何在不定义我自己的数据类型的情况下获得更好的精度,这显然会牺牲性能?

您需要自己编写(当然是学习经验,最好不要为生产代码做)或使用库,如GNU MPFRBoost.Multiprecision


1
投票

除了必须满足的最低级别之外,C ++标准没有为浮点类型设置固定的要求。

可能您使用的C ++实现针对的是英特尔处理器。除了常见的IEEE-754基本32位和64位二进制浮点格式外,英特尔还采用80位格式。您的C ++实现可能正在使用long double

英特尔的80位格式有效位数比16位double格式多11位。 (它实际上使用64,其中double格式使用52,但其中一个保留用于显式前导1.)11个更多位意味着211 = 2048倍有效数值,这是大约三个十进制数字。

80位格式(十个字节)优先与16个字节的倍数对齐,因此包含六个字节的填充以使long double大小为16个字节的倍数。


1
投票

您的问题中有许多不正确的假设

首先,对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没有这么广泛的支持,因为很少有人需要更高的精度而不是双倍,而硬件的成本要高得多。因此,一些平台将其映射到IEEE-754双精度,即与double相同。如果底层硬件支持,其他一些人将其映射到80-bit IEEE 754 extended-precision format。否则long double将由double-double arithmeticIEEE-754 quadruple-precision代表

此外,精度也不能线性地扩展到类型中的位数。很容易看出double的精度是float的两倍,而float的范围是trap representations的8倍,尽管存储量只有两倍,因为它有53位有效数,而浮点数为24位,指数位数为3位。类型也可以有std::numeric_limits<long double>::digits或填充位,因此不同的类型可能具有不同的范围,即使它们具有相同的大小并且属于相同的类别(整数或浮点)

所以重要的是long double。如果你打印出来,你会发现double有64位有效数,只比See it live高11位。 long double。这意味着您的编译器对gcc has various options使用80位扩展精度,其余的只是填充字节以保持对齐。实际上-malign-double会改变你的输出:

  • -mno-align-doublelong double控制-m96bit-long-double的排列
  • -m128bit-long-double-mlong-double-64用于改变填充尺寸
  • -mlong-double-80-mlong-double-128long double控制潜在的long double实施

通过更改选项,您将获得-mlong-double-128的以下结果

如果禁用填充,您将获得size = 10,但由于未对齐,这将导致性能损失

在PowerPC中你也可以-mabi=ibmlongdouble。使用-mabi=ieeelongdouble(双倍算术,这是默认值)你得到(大小,数字10,数字2)=(16,31,106)但是使用https://en.wikipedia.org/wiki/Long_double元组将变为(16,33,113)

有关更多信息,请阅读arbitrary-precision arithmetic

而且我还想知道如何在不定义自己的数据类型的情况下获得更好的精度

要搜索的关键字是List of arbitrary-precision arithmetic software。您可以在找到各种各样的库。你可以在标签或qazxswpoi找到更多的信息。

© www.soinside.com 2019 - 2024. All rights reserved.