今天在读一本参考书的时候我遇到了一个例子 -
echo (int) ((0.1 + 0.7) * 10); // output 7
由于((0.1 + 0.7) * 10)
内部评估为7.999999
,而转换为int
时,结果为7
。
我也发现它是正确的。请参阅 Codepad 。
但是当我尝试更多的例子时,我发现了一些奇怪的东西,比如 -
echo (int) ((0.2 + 0.7) * 10); // output 9
( Codepad )
echo (int) ((0.7 + 0.7) * 10); // output 14
( Codepad )
还有更多。每次我改变价值观,它都给了我正确的答案。
我想知道为什么只有((0.1 + 07) * 10)
产生的结果与其他结果不同。
真的很奇怪还是我错过了什么?
答案 0 :(得分:3)
在通用双精度格式中,数字用符号位,11位指数和53位小数部分表示,称为有效数。有效数字总是一个53位的非负整数除以2 52 (它也可以二进制形式写成一个二进制数字,一个基数点和52个二进制数字)。
.1无法准确表示。它用-4的指数表示,有效数为7205759403792794/2 52 。也就是说,最接近的double
到.1是7205759403792794•2 -52 •2 -4 = 0.1000000000000000055511151231257827021181583404541015625。
最接近的double
到.7的有效位数为6305039478318694/2 52 ,指数为-1;它是6305039478318694•2 -52 •2 -1 = 0.6999999999999999555910790149937383830547332763671875。
添加这两个数字时,结果为0.7999999999999999611421941381195210851728916168212890625。这在double
中也不完全可以表示;它必须四舍五入到最接近的可表示值,当你乘以10时,必须再次舍入。但是,您可以看到总和小于.8。最终结果小于8,因此转换为整数会将其截断为7。
double
,最近的.8是0.8000000000000000444089209850062616169452667236328125。将其添加到0.1000000000000000055511151231257827021181583404541015625时,总和为0.9000000000000000499600361081320443190634250640869140625。如你所见,它大于.9。舍入和乘以10的最终结果将是9或更大,因此转换为整数会产生9。
你尝试的其他几个值没有向下舍入的事实仅仅是偶然事件。每个不完全可表示的值都介于两个可表示的值之间,一个较高,一个较低。有些更接近较高值,有些接近较低值,而您恰好选择了接近较高可表示值且向上舍入的值。
答案 1 :(得分:1)
见PHP doc:
永远不要将未知分数转换为整数,因为这有时会导致意外结果。
<?php
echo (int) ( (0.1+0.7) * 10 ); // echoes 7!
?>
所以是的,结果是奇怪的'因为意外(内部表示(0.1 + 0.7)* 10就像7.99999 ......)
有关PHP文档中Floating Point Precision的更多信息。
答案 2 :(得分:0)
这是因为使用花车时圆角不精确 例如:
0.1 + 0.7 = 0.7999999999
0.7999999999 * 10 = 7.999999999
floor(7.999999999) = 7
解决这个问题的方法是在进行类型转换之前进行舍入。
答案 3 :(得分:0)
计算机处理绝对数量;他们不能在比特级明确表示一个分数。这是使用floating-point representation来解决的,{{3}}是一种表示实数近似值的方法。
不幸的是,有些数字不可能准确地表示100%,这在处理浮点运算时会导致不精确。
有关精确原因的详细信息,请对浮点表示进行一些研究。但这是一个相当数学的技术主题。
修改强>
让我澄清一下。我们都知道计算机处理二进制。它们读取,写入和处理1或0的位。
32位处理器通常将内存划分为4字节的块。因此,int和float的默认大小是4个字节,或32位。用二进制表示整数(int)很容易。数字8是:00000000 00000000 00000000 00001000.但计算机如何表示十进制数?请记住,它只能看到1和0;它不能只放置一个“。”在他们中间!
定点表示(例如,说前16位是整数值(在。之前的部分),第二16位是小数值)显着限制了可以表示的数字范围,因为它减少了最大数量为16位int并可能浪费“。”之后的所有位。这可能不需要。
因此,计算机使用称为浮点表示的技术,其中使用编码指数缩放数字。因此,部分数字是指数,部分是分数。与定点表示法相比,这大大增加了可能数字的范围。但有些数字无法表示完全精确。
这就是为什么处理货币的任何计算机系统永远不会将值存储为浮点数(例如,1.10英镑将始终存储为110p)。任何精度至关重要的系统都应该执行与int相同的算术运算,并作为最后一步进行浮点运算。
请注意,这不仅仅是一个PHP问题,它存在于所有语言中。例如,JavaScript:
alert((0.1+0.7)*10); // alerts 7.9999999999
答案 4 :(得分:-1)
使用intval
代替,它更可靠。