让我们考虑以下代码:
#include <stdio.h> int main() { float x = 0.33; printf("%.100f",x); return 0; }
如果float的精度是6位,那么printf怎么可能显示6位以上的数字呢?
printf
u91tlkcl1#
您尝试将小数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?了解更多信息。
float
0.0101010001111010111000011
0.0101010101010101010101011000000000
%.100f
double
ercv8c1e2#
“如果float有6位数的精度...”-->是一个弱前提。常见的float'没有6位的 decimal 精度,而是24位的 binary 精度。如何用printf(?)当以十进制打印一个二进制浮点数时,每个二进制数字贡献一些2的幂,如...,16,8,4,2,1,0.5,0.25,0.125,0.0625,...这些2的幂的总和可以容易地超过6个十进制数字。在极端情况下,FLT_TRUE_MIN通常具有以下精确值:
float'
FLT_TRUE_MIN
0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125
小数点后9位有效数字很少有比这更重要的。
km0tfn4u3#
如果float有6位精度,为什么我们可以用printf显示超过6位的浮点数?float的每个定义没有6位精度。您已经选择显示比实现可能提供的数字更多的数字-它提供了。为什么我们可以用printf显示超过6位的浮点数?你可以告诉程序显示float/double/long double中的任何内容,它仍然是一个近似值。
long double
比较:https://en.wikipedia.org/wiki/IEEE_754
3条答案
按热度按时间u91tlkcl1#
您尝试将小数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?了解更多信息。ercv8c1e2#
“如果float有6位数的精度...”-->是一个弱前提。
常见的
float'
没有6位的 decimal 精度,而是24位的 binary 精度。如何用
printf
(?)当以十进制打印一个二进制浮点数时,每个二进制数字贡献一些2的幂,如...,16,8,4,2,1,0.5,0.25,0.125,0.0625,...
这些2的幂的总和可以容易地超过6个十进制数字。
在极端情况下,
FLT_TRUE_MIN
通常具有以下精确值:小数点后9位有效数字很少有比这更重要的。
km0tfn4u3#
如果float有6位精度,为什么我们可以用
printf
显示超过6位的浮点数?float
的每个定义没有6位精度。您已经选择显示比实现可能提供的数字更多的数字-它提供了。为什么我们可以用
printf
显示超过6位的浮点数?你可以告诉程序显示
float
/double
/long double
中的任何内容,它仍然是一个近似值。比较:https://en.wikipedia.org/wiki/IEEE_754