我是计算机工程专业的学生,并为BYU-Idaho的入门C ++课程做辅导,一名学生成功地困扰了我。
如果为此编写代码:
#include <iostream>
using namespace std;
int main()
{
float y = .59;
int x = (int)(y * 100.0);
cout << x << endl;
return 0;
}
结果= 58
#include <iostream>
using namespace std;
int main()
{
double y = .59;
int x = (int)(y * 100.0);
cout << x << endl;
return 0;
}
结果= 59
我告诉他这是一个精确的问题,因为int比浮子更精确,它会丢失信息。双精度比浮点精确,所以它的工作原理。
但是我不确定我所说的是否正确。我认为这与使用零填充的int有关,因此当它被转换时会被“截断”,但我不确定。
如果你们中的任何一个人想要解释所有这些“下面”发生了什么,我会发现它很有趣!
答案 0 :(得分:7)
问题是float
不足以保持完全值0.59。如果存储这样的值,它将以二进制表示(已在编译期间)舍入为不同的值,在您的情况下,这是一个略小于0.59的值(它可能稍微大于您希望它的值) )。将此值乘以100时,得到的值略小于59.将此值转换为整数会将其向0 舍入,因此这将导致58。
0.59作为浮点数将被存储为(现在表示为人类可读的十进制数字):
0.589999973773956298828125
现在为double
类型。虽然此类型具有基本相同的问题,但可能有两个原因可以获得预期结果:double
可以保存您想要的完全值(0.59不是这种情况)但对于其他值(可能是这种情况),或者编译器决定将其向上。因此,将此值乘以100会导致不小于59的值,并按预期将其舍入为0到59.
现在请注意,编译器仍然可以将0.59作为double
向下舍入。的确,我刚刚检查过它。作为double
的0.59将存储为:
0.58999999999999996891375531049561686813831329345703
但是,在将其转换为整数之前,您将此值乘以100 。现在有一个有趣的观点:当乘以100时,编译器放置的y
与0.59的差值已消除,因为0.59 * 100可能无法再精确存储。实际上,处理器会计算0.58999999999999996891375531049561686813831329345703 * 100.0
,它将向上舍入到59,这个数字可以在double
中表示!
有关详细信息,请参阅此代码:http://ideone.com/V0essb
现在你可能想知道为什么同样不计算float
,它应该表现完全相同但精度不同。问题是0.589999973773956298828125 * 100.0
不向上舍入为59
(也可以在float
中表示)。计算后的舍入行为并未真正定义。
实际上,对浮点数的操作并没有精确指定,这意味着您可以在不同的机器上遇到不同的结果。这样可以实现性能调整,导致稍微不正确的结果,即使不涉及舍入!情况可能是在另一台机器上你最终得到了预期的结果,而在其他机器上则不是。
答案 1 :(得分:2)
0.59在二进制浮点中不能完全表示。因此x
实际上是一个略高于或低于0.59的值。您使用float
或double
可能会受到影响。这反过来将决定你的程序的结果是58还是59。
答案 2 :(得分:2)
这是一个精确的问题,与.59
的事实有关
在double
或float
中无法准确表示。
因此y
不 .59
;它与.59
非常接近
(略多或略少)。乘以100
确切地说,但由于原始价值不是,你会得到一些东西
略高于或略低于59
。转换为int
截断为零,因此您可以获得59
或58
。
答案 3 :(得分:0)
这与使用旧计算器执行1 / 3 * 3
时的问题相同,并且会出现2.9999999
或类似内容。将此与(int)(float_value)
将简单地删除小数的事实相结合,因此,如果floatvalue
是58.999996185
,就像我的机器得到的那样,那么58
将是结果,因为虽然58.999996185
近59,如果你只剪出前两位,那确实是58.
浮点数非常适合计算很多东西,但在“结果是什么”时你必须非常小心。它是近似值,精度不是无限的,并且将发生中间结果的舍入。
使用double,有更多的数字,很可能是当计算0.58999999999999乘以100时,最后一位是1而不是0,所以结果是59.00000000001或类似的东西,然后变成59作为整数。