为什么浮点数据类型的精度与其大小成正比?例如。:
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
大小的两倍。
但这与double
和long double
之间的情况不同,long double
的大小是128位,是64位double
的两倍,但它的精度只有三位数!
我不知道浮点数是如何实现的,但从理性的角度来看,使用64位以上的内存只有三位精度才有意义?!
我四处搜索,但无法找到一个简单明了的答案。如果有人可以解释为什么long double
的精度只比double
高三位数,你能解释为什么这与double
和float
之间的情况不一样吗?
而且我还想知道如何在不定义我自己的数据类型的情况下获得更好的精度,这显然会牺牲性能?
“精度”并不是浮点值的全部。它也是关于“量级”(不确定该术语是否正确!):所表示的值有多大(或小)?
为此,尝试打印每种类型的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 MPFR或Boost.Multiprecision。
除了必须满足的最低级别之外,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个字节的倍数。
您的问题中有许多不正确的假设
首先,对C ++中类型的大小没有要求。该标准仅规定了每种类型的最低精度,并且......
...类型
double
提供至少与float
一样多的精度,而long double
类型至少提供与double
一样多的精度。float
类型的值集合是double
类型的值集合的子集;double
类型的值集合是long double
类型的值集合的子集。浮点类型的值表示是实现定义的。http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf
大多数现代实现将float
和double
映射到IEEE-754单精度和双精度格式,因为它们的硬件支持是主流。然而long double
没有这么广泛的支持,因为很少有人需要更高的精度而不是双倍,而硬件的成本要高得多。因此,一些平台将其映射到IEEE-754双精度,即与double
相同。如果底层硬件支持,其他一些人将其映射到80-bit IEEE 754 extended-precision format。否则long double
将由double-double
arithmetic或IEEE-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-double
和long double
控制-m96bit-long-double
的排列-m128bit-long-double
和-mlong-double-64
用于改变填充尺寸-mlong-double-80
,-mlong-double-128
和long double
控制潜在的long double
实施通过更改选项,您将获得-mlong-double-128
的以下结果
-m96bit-long-double
:size = 16,digits10 = 33,digits2 = 113-mlong-double-64
:size = 12,digits10 = 18,digits2 = 64如果禁用填充,您将获得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。您可以在bigint找到各种各样的库。你可以在标签biginteger,arbitrary-precision或qazxswpoi找到更多的信息。