英文:
If float has 6 digits of precision, why can we display more than 6 digits of floats with printf?
问题
以下是要翻译的内容:
如果浮点数具有6位精度,如何使用printf
显示超过6位的数字?
英文:
Let us consider the following piece of code:
#include <stdio.h>
int main()
{
float x = 0.33;
printf("%.100f",x);
return 0;
}
If float has 6 digits of precision, how is it possible to display more than 6 digits with printf
?
答案1
得分: 6
你尝试将十进制小数0.33转换为float
。但是,像大多数十进制小数一样,数字0.33无法在float
类型内部使用的二进制表示中精确表示。你能够得到的最接近的二进制小数是0.0101010001111010111000011
。如果我们将这个二进制小数转换回十进制,它恰好是0.3300000131130218505859375。
在十进制中,如果我告诉你有7位有效数字,然后你尝试表示1/3 = 0.333…这个数字,你期望得到0.333333300000。也就是说,你期望获得一些与你原始数字匹配的有效数字,然后在没有足够有效数字的地方得到0。而二进制小数的工作方式也相同:对于float
类型,二进制小数始终有确切的24位有效数字,然后(如果你愿意的话)可以跟随任意数量的二进制0。
当我们将那个二进制数转换回十进制时,我们得到了大约7位与我们原来认为的十进制数匹配的数字,然后不是零,而是看起来像随机数字。例如,1/3作为二进制的float
是0.0101010101010101010101011000000000
(注意24位有效位),当转换为十进制时是0.333333343267440795898437500000(注意7位准确数字)。
当你听说float
类型具有约7位有效数字时,这并不意味着你会得到7位你原始数字的数字,后面都是0。它的意思是你将得到大约7位你原始数字的数字(也许是6位,或者8位,或者9位或更多),然后跟随着一些数字,这些数字可能不匹配你的原始数字,但也不都是0。但这实际上不是问题,特别是如果你按照有用的数字位数四舍五入后将这个数字打印出来,这是推荐的和正确的做法。这可能会成为问题(虽然经常会出现这种情况),是因为你以非常多的数字位数打印出这个数字,使用类似%.100f
的格式,然后看到一些看起来奇怪的数字,它们并不都是0,这会让你感到困惑,就像这里一样。
float
和double
类型使用二进制表示内部导致了无数的意外,就像这个例子一样。这个表示是二进制的并不令人惊讶(我们都知道计算机在二进制中执行所有操作),但二进制小数无法准确表示我们习惯的十进制小数,这真的令人惊讶。关于这个问题,可以参考标准的SO问题Is floating point math broken?了解更多信息。
英文:
You tried to convert the decimal fraction 0.33 to a float
. But, like most decimal fractions, the number 0.33 cannot be represented exactly in the binary representation used internally by type float
. The closest you can get is the binary fraction 0.0101010001111010111000011
. That fraction, if we convert it back to decimal, is exactly 0.3300000131130218505859375.
In decimal, if I tell you that you have 7 digits worth of significance, and you try to represent the number 1/3 = 0.333…, you expect to get 0.333333300000. That is, you expect to get some number of significant digits matching your original number, followed by 0's where there wasn't enough significance. And binary fractions work the same way: for type float
, the binary fraction always has exactly 24 bits of significance, followed (if you like) by any number of binary 0's.
When we convert that binary number back to decimal, we get approximately 7 digits matching the decimal number we thought we had, followed not by zeroes, but rather, by what look like random digits. For example, 1/3 as a binary float
is 0.0101010101010101010101011000000000
(note 24 significant bits), which when converted to decimal is 0.333333343267440795898437500000 (note 7 accurate digits).
When you hear that type float
has approximately 7 digits of significance, that does not mean you'll get 7 digits of your original number, followed by 0's. What it means is that you'll get approximately 7 digits of your original number (but maybe 6, or maybe 8 or 9 or more), followed by some digits which probably don't match your original number but which aren't all 0, either. But that's not actually a problem, especially if (as is recommended and proper) you print this number back out rounded to a useful number of digits. When it can be a problem (though this comes up a lot) is when you print the number back out with a non-useful number of digits, with a format like %.100f
, and you see some strange-looking digits which aren't all 0, and this perplexes you, as it did here.
The fact that types float
and double
use a binary representation internally leads to endless surprises like this. It's not surprising that the representation is binary (we all know computers do everything in binary), but the inability of binary fractions to accurately represent the decimal fractions we're used to, now that's really surprising. See the canonical SO question Is floating point math broken? for more on this.
答案2
得分: 2
> 如果浮点数有6位精度..." --> 是一个薄弱的前提。
通用float
并非具有6位_十进制_精度,而是24位_二进制_精度。
> 如何通过printf
显示超过6位的数字(?)
在以十进制打印二进制浮点数时,每个二进制数字都贡献一些2的幂,如..., 16, 8, 4, 2, 1, 0.5, 0.25, 0.125, 0.0625, ...
这些2的幂的总和很容易超过6位十进制数字。
在极端情况下,FLT_TRUE_MIN
通常具有确切值:
0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125
很少有超过9个有效的十进制数字是重要的。
英文:
> "If float has 6 digits of precision ..." --> is a weak premise.
Common float'
does not have 6 digits of decimal precision, but 24 digits of binary precision.
> how is it possible to display more than 6 digits with printf
(?)
When printing a binary floating point number in decimal, each binary digit contributes some power-of-2 like ..., 16, 8, 4, 2, 1, 0.5, 0.25, 0.125, 0.0625, ...
The sum of those powers-of-2 can readily exceed 6 decimal digits.
In the extreme, FLT_TRUE_MIN
often has the exact value of:
0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125
Rarely are more the 9 significant decimal digits important.
答案3
得分: 0
If float has 6 digits of precision, why can we display more than 6 digits of floats with printf
?
如果浮点数有6位精度,为什么我们可以使用 printf
显示超过6位的浮点数?
A float
doesn't have 6 digits of precision per definition. You've opted to display more digits than the implementation can possibly provide - and it provides.
按定义,一个 float
并不具有6位精度。您选择显示比实现可能提供的更多数字 - 它提供了。
Why can we display more than 6 digits of floats with printf
?
为什么我们可以使用 printf
显示超过6位的浮点数?
You can tell the program to display whatever you have in a float
/double
/long double
and it's still expected to be an approximation.
您可以让程序显示 float
/double
/long double
中的任何内容,但仍然应该是一个近似值。
Displaying the current content of such a variable is best done when debugging.
在调试时,显示这样一个变量的当前内容是最好的做法。
Comparing: https://en.wikipedia.org/wiki/IEEE_754
参考:https://en.wikipedia.org/wiki/IEEE_754
英文:
> If float has 6 digits of precision, why can we display more than 6 digits of floats with printf
?
A float
doesn't have 6 digits of precision per definition. You've opted to display more digits than the implementation can possibly provide - and it provides.
> why can we display more than 6 digits of floats with printf
?
You can tell the program to display whatever you have in a float
/double
/long double
and it's still expected to be an approximation.
Displaying the current content of such a variable is best done when debugging.
Comparing: https://en.wikipedia.org/wiki/IEEE_754
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论