为什么浮点变量会以一种奇怪的方式截取小数点后面的数字来保存值?

9

我有这样一行简单的代码:

float val = 123456.123456;

当我打印这个值或查看作用域时,它存储的值为123456.13。

好吧,这是可以理解的,它不能在只有4个字节的情况下存储小数点后面的所有数字,但为什么它会变成小数点后13位呢?难道不应该是12位吗?

(使用vc++ 2010 express在win32上)


感谢大家的回答,我现在明白了。我只是认为应该在小数点后舍去超限的数字,而不是四舍五入。 - Kosmo零
5个回答

10

123456.123456在二进制下是11110001001000000.000111111001...(无限)它四舍五入为11110001001000000.001,即123456.125。 这个数 在打印时四舍五入为123456.13。


8

val存储的值等于123456.125。你得到了.13是因为进行了四舍五入处理:

float val = 123456.123456;
printf("%.4f %.2f\n", val, val);

输出:123456.1250 123456.13

在这种情况下,应该使用double以避免截断。编译器也应该警告你:"warning C4305: 'initializing' : truncation from 'double' to 'float'"


7

当以浮点数表示时,您的数字具有16的指数(即该值为其尾数乘以2 ^ 16或65536)。然后,尾数变为

123456.123456 / 65536 = 1.8837909462890625

为了适应32位浮点数,尾数被截断为23位,因此现在变为1.883791。当乘以65536后,它变成123456.125
请注意小数点后第三个位置的5:你使用的C++输出例程将其四舍五入,使得最终数字看起来像是123456.13
编辑:关于四舍五入的解释(Rick Regan的评论)
首先在二进制中进行四舍五入(到24位),在十进制转换为二进制时,然后在printf中转换为十进制。存储值为1.1110001001000000001 x 2^16 = 1.8837909698486328125 x 2^16 = 123456.125。它打印为123456.13,但只是因为Visual C++使用“从零开始向上四舍五入”的舍入方式。
Rick在这个主题上有一篇杰出的文章
如果您想玩其他数字及其浮点表示法,请使用非常有用的IEEE-754计算器

能否请您再详细解释一下?我无法理解这句话的特别之处:“当以浮点数表示时,您的数字具有指数16”。为什么需要这样做? - Rasmi Ranjan Nayak
@RasmiRanjanNayak 32位浮点数由23位尾数、7位二进制指数和一个符号位表示。逻辑上,您将原始数字除以2,并增加指数,直到小数点前仅剩下“1”这一位数字。对于“123456.123456”,需要进行16次除法运算。 - Sergey Kalinichenko
3
最好这个描述是具有误导性的。在十进制转二进制时,先进行了二进制舍入(到24位),然后在printf中转换为十进制。存储的值是1.1110001001000000001 x 2 ^ 16 = 1.8837909698486328125 x 2 ^ 16 = 123456.125。它打印输出为123456.13,但这只是因为Visual C ++使用"四舍五入到最近的偶数"舍入(请参见我的文章http://www.exploringbinary.com/inconsistent-rounding-of-printed-floating-point-numbers/)。 - Rick Regan
1
@RickRegan 哇,好文!感谢您的参考,我将您的评论添加到答案中,使其更加完整。非常感谢您的帮助! - Sergey Kalinichenko

2
尝试打印std::numeric_limits<float>::digits10的值。这大致表示浮点数在十进制中具有多少精度。您正在尝试超出它,因此会出现精度损失(这意味着重要数字之外的数字实际上并不重要)。
例如,请参见numeric_limits<double>::digits10的含义是什么

2

这完全取决于编译器。在GCC中检查它。应该是xxx.12。


5
如果内部使用的格式是IEEE-754,那么它就不应该与编译器有任何关系。 - Matteo Italia
我觉得我们对“编译器相关”这个词给出了两种不同的解释;我和其他人的理解是你在说“它在不同的编译器中会有所不同是正常的”。而你是指这是一个特定于编译器的错误吗? - Matteo Italia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接