C++ 双精度 - 小数位

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9999221/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 13:29:53  来源:igfitidea点击:

Double precision - decimal places

c++cprecisionieee-754

提问by nf313743

From what I have read, a value of data type double has an approximate precision of 15 decimal places. However, when I use a number whose decimal representation repeats, such as 1.0/7.0, I find that the variable holds the value of 0.14285714285714285 - which is 17 places (via the debugger).

根据我的阅读,数据类型为 double 的值的近似精度为 15 位小数。但是,当我使用十进制表示重复的数字(例如 1.0/7.0)时,我发现该变量的值是 0.14285714285714285 - 这是 17 位(通过调试器)。

I would like to know why it is represented as 17 places internally, and why a precision of 15 is always written at ~15?

我想知道为什么它在内部表示为17位,为什么精度15总是写在~15?

回答by Keith Thompson

An IEEE double has 53 significant bits (that's the value of DBL_MANT_DIGin <cfloat>). That's approximately 15.95 decimal digits (log10(253)); the implementation sets DBL_DIGto 15, not 16, because it has to round down. So you have nearly an extra decimal digit of precision (beyond what's implied by DBL_DIG==15) because of that.

IEEE double 有 53 个有效位(即DBL_MANT_DIGin的值<cfloat>)。这大约是 15.95 个十进制数字 (log10(2 53));实现设置DBL_DIG为 15,而不是 16,因为它必须四舍五入。因此,因此您几乎有一个额外的十进制数字精度(超出 所暗示的DBL_DIG==15)。

The nextafter()function computes the nearest representable number to a given number; it can be used to show just how precise a given number is.

nextafter()函数计算与给定数字最接近的可表示数字;它可用于显示给定数字的精确程度。

This program:

这个程序:

#include <cstdio>
#include <cfloat>
#include <cmath>

int main() {
    double x = 1.0/7.0;
    printf("FLT_RADIX = %d\n", FLT_RADIX);
    printf("DBL_DIG = %d\n", DBL_DIG);
    printf("DBL_MANT_DIG = %d\n", DBL_MANT_DIG);
    printf("%.17g\n%.17g\n%.17g\n", nextafter(x, 0.0), x, nextafter(x, 1.0));
}

gives me this output on my system:

在我的系统上给我这个输出:

FLT_RADIX = 2
DBL_DIG = 15
DBL_MANT_DIG = 53
0.14285714285714282
0.14285714285714285
0.14285714285714288

(You can replace %.17gby, say, %.64gto see more digits, none of which are significant.)

(可以替换%.17g,比方说,%.64g看到更多的数字,其中没有一个是显著。)

As you can see, the last displayed decimal digit changes by 3 with each consecutive value. The fact that the last displayed digit of 1.0/7.0(5) happens to match the mathematical value is largely coincidental; it was a lucky guess. And the correct roundeddigit is 6, not 5. Replacing 1.0/7.0by 1.0/3.0gives this output:

如您所见,最后显示的十进制数字随每个连续值变化 3。1.0/7.0( 5)的最后一位显示数字恰好与数学值相符,这在很大程度上是巧合;这是一个幸运的猜测。正确的四舍五入数字是6,不是5。更换1.0/7.01.0/3.0给出了这样的输出:

FLT_RADIX = 2
DBL_DIG = 15
DBL_MANT_DIG = 53
0.33333333333333326
0.33333333333333331
0.33333333333333337

which shows about 16 decimal digits of precision, as you'd expect.

正如您所期望的那样,它显示了大约 16 位十进制数字的精度。

回答by trutheality

It is actually 53 binary places, which translates to 15 stable decimal places, meaning that if you round a start out with a number with 15 decimal places, convert it to a double, and then round the doubleback to 15 decimal places you'll get the same number. To uniquely represent a doubleyou need 17 decimal places (meaning that for every number with 17 decimal places, there's a unique closest double) which is why 17 places are showing up, but not all 17-decimal numbers map to different doublevalues (like in the examples in the other answers).

它实际上是 53 个二进制位,转换为 15 个稳定的小数位,这意味着如果您从一个有 15 个小数位的数字开始,将其转换为 a double,然后将其double返回到 15 个小数位,您将得到相同的数字。要唯一地表示 a,double您需要 17 个小数位(意味着对于每个具有 17 个小数位的数字,都有一个唯一的最接近的double),这就是为什么显示 17 个位,但并非所有 17 位小数都映射到不同的double值(如示例中所示)在其他答案中)。

回答by John Calsbeek

Decimal representation of floating point numbers is kind of strange. If you have a number with 15 decimal places and convert that to a double, then print it out with exactly 15 decimal places, you should get the same number. On the other hand, if you print out an arbitrary doublewith 15 decimal places and the convert it back to a double, you won't necessarily get the same value back—you need 17decimal places for that. And neither 15 nor 17 decimal places are enough to accurately display the exact decimal equivalent of an arbitrary double. In general, you need over 100 decimal placesto do that precisely.

浮点数的十进制表示有点奇怪。如果您有一个小数点后 15 位的数字并将其转换为 a double,然后将其打印出来精确到小数点后 15 位,您应该得到相同的数字。另一方面,如果您打印出double带有 15 位小数的任意值并将其转换回 a double,您不一定会得到相同的值——为此您需要17位小数。并且 15 位或 17 位小数都不足以准确显示任意double. 一般来说,您需要超过 100 个小数位才能精确做到这一点。

See the Wikipedia page for double-precisionand this article on floating-point precision.

有关双精度的信息,请参阅Wikipedia 页面有关浮点精度的这篇文章

回答by Mooing Duck

A double holds 53 binary digits accurately, which is ~15.9545898 decimal digits. The debugger can show as many digits as it pleases to be more accurate to the binaryvalue. Or it might take fewerdigits and binary, such as 0.1 takes 1 digit in base 10, but infinite in base 2.

double 准确地保存了 53 个二进制数字,即 ~15.9545898 个十进制数字。调试器可以显示尽可能多的数字,以便更准确地显示二进制值。或者它可能需要更少的数字和二进制,例如 0.1 在基数 10 中需要 1 个数字,但在基数 2 中是无限的。

This is odd, so I'll show an extreme example. If we make a super simple floating point value that holds only 3 binary digits of accuracy, and no mantissa or sign (so range is 0-0.875), our options are:

这很奇怪,所以我将展示一个极端的例子。如果我们创建一个超级简单的浮点值,它只包含 3 个精度的二进制数字,并且没有尾数或符号(因此范围是 0-0.875),我们的选项是:

binary - decimal
000    - 0.000
001    - 0.125
010    - 0.250
011    - 0.375
100    - 0.500
101    - 0.625
110    - 0.750
111    - 0.875

But if you do the numbers, this format is only accurate to 0.903089987 decimal digits. Not even 1 digit is accurate. As is easy to see, since there's no value that begins with 0.4??nor 0.9??, and yet to display the full accuracy, we require 3 decimal digits.

但是如果你做数字,这种格式只能精确到 0.903089987 十进制数字。甚至 1 位数都不准确。很容易看出,由于没有以0.4??nor开头的值0.9??,而且还没有显示完整的精度,我们需要 3 个十进制数字。

tl;dr: The debugger shows you the value of the floating point variableto some arbitrary precision (19 digits in your case), which doesn't necessarily correlate with the accuracy of the floating point format (17 digits in your case).

tl; dr:调试器以任意精度(在您的情况下为 19 位)向您显示浮点变量的值,这不一定与浮点格式的准确性(在您的情况下为 17 位)相关。

回答by Jerry Coffin

IEEE 754 floating point is done in binary. There's no exact conversion from a given number of bits to a given number of decimal digits. 3 bits can hold values from 0 to 7, and 4 bits can hold values from 0 to 15. A value from 0 to 9 takes roughly3.5 bits, but that's not exact either.

IEEE 754 浮点数以二进制形式完成。从给定的位数到给定的十进制数字没有精确的转换。3 位可以保存 0 到 7 的值,4 位可以保存 0 到 15 的值。 0 到 9 的值大约需要3.5 位,但这也不准确。

An IEEE 754 double precision number occupies 64 bits. Of this, 52 bits are dedicated to the significand (the rest is a sign bit and exponent). Since the significand is (usually) normalized, there's an implied 53rdbit.

IEEE 754 双精度数占用 64 位。其中,52 位专用于有效数(其余为符号位和指数)。由于尾数(通常)归一化,有一个隐含的53位。

Now, given 53 bits and roughly 3.5 bits per digit, simple division gives us 15.1429 digits of precision. But remember, that 3.5 bits per decimal digit is only an approximation, not a perfectly accurate answer.

现在,给定 53 位和大约每位 3.5 位,简单的除法给我们 15.1429 位的精度。但请记住,每个十进制数字 3.5 位只是一个近似值,并不是一个完全准确的答案。

Many (most?) debuggers actually look at the contents of the entire register. On an x86, that's actually an 80-bit number. The x86 floating point unit will normally be adjusted to carry out calculations to 64-bit precision -- but internally, it actually uses a couple of "guard bits", which basically means internally it does the calculation with a few extra bits of precision so it can round the last one correctly. When the debugger looks at the whole register, it'll usually find at least one extra digit that's reasonably accurate -- though since that digit won't have any guard bits, it may not be rounded correctly.

许多(大多数?)调试器实际上查看整个寄存器的内容。在 x86 上,这实际上是一个 80 位数字。x86 浮点单元通常会被调整为执行 64 位精度的计算——但在内部,它实际上使用了几个“保护位”,这基本上意味着它在内部以一些额外的精度进行计算,所以它可以正确舍入最后一个。当调试器查看整个寄存器时,它通常会找到至少一个相当准确的额外数字——尽管由于该数字没有任何保护位,因此可能无法正确舍入。

回答by spencercw

It is because it's being converted from a binary representation. Just because it has printed all those decimal digits doesn't mean it can represent all decimal values to that precision. Take, for example, this in Python:

这是因为它是从二进制表示转换而来的。仅仅因为它打印了所有这些十进制数字并不意味着它可以以该精度表示所有十进制值。以 Python 中的这个为例:

>>> 0.14285714285714285
0.14285714285714285
>>> 0.14285714285714286
0.14285714285714285

Notice how I changed the last digit, but it printed out the same number anyway.

请注意我如何更改最后一位数字,但无论如何它打印出相同的数字。

回答by supercat

In most contexts where doublevalues are used, calculations will have a certain amount of uncertainty. The difference between 1.33333333333333300 and 1.33333333333333399 may be less than the amount of uncertainty that exists in the calculations. Displaying the value of "2/3 + 2/3" as "1.33333333333333" is apt to be more meaningful than displaying it as "1.33333333333333319", since the latter display implies a level of precision that doesn't really exist.

在大多数使用double值的情况下,计算将具有一定的不确定性。1.3333333333333300 和 1.33333333333333399 之间的差异可能小于计算中存在的不确定性量。将 "2/3 + 2/3" 的值显示为 "1.33333333333333" 往往比将其显示为 "1.33333333333333319" 更有意义,因为后者的显示意味着一个并不真正存在的精度级别。

In the debugger, however, it is important to uniquely indicate the value held by a variable, including essentially-meaningless bits of precision. It would be very confusing if a debugger displayed two variables as holding the value "1.333333333333333" when one of them actually held 1.33333333333333319 and the other held 1.33333333333333294 (meaning that, while they looked the same, they weren't equal). The extra precision shown by the debugger isn't apt to represent a numerically-correct calculation result, but indicates how the code will interpret the values held by the variables.

但是,在调试器中,重要的是要唯一地指示变量保存的值,包括基本无意义的 precision 位。如果调试器将两个变量显示为持有值“1.333333333333333”,而其中一个实际持有 1.333333333333333319 而另一个持有 1.33333333333333294(这意味着,虽然它们看起来并不相同),那将会非常令人困惑。调试器显示的额外精度并不容易代表数字正确的计算结果,但表明代码将如何解释变量所保存的值。