浮点计算使用float而不是double来得到不同的结果

时间:2013-02-15 07:51:48

标签: c++ floating-point double floating-accuracy

我有以下代码行。

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()));
  • void onBeingHit(int decHP)方法接受整数并更新健康点。
  • float getDefensePercent()方法是一个getter方法,返回英雄的防御百分比。
  • ENEMY_ATTACK_POINT是定义为#define ENEMY_ATTACK_POINT 20的宏常量因子。

假设hero->getDefensePercent()返回0.1。所以计算是

20 * (1.0 - 0.1)  =  20 * (0.9)  =  18

每当我使用以下代码尝试时(没有f追加1.0

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()));

17

但是对于以下代码(f之后附加1.0

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0f - hero->getDefensePercent()));

18

发生了什么事?虽然f已经处于浮动状态,但hero->getDefensePercent()有意义吗?

3 个答案:

答案 0 :(得分:11)

发生了什么事?在这两种情况下,为什么不是整数结果18

问题在于,当转换为整数值时,浮点表达式的结果将舍入为零(在这两种情况下)。

0.1不能完全表示为浮点值(在两种情况下都是如此)。编译器将转换为二进制IEEE754浮点数,并决定是向上舍入还是向下舍入为可表示的值。然后处理器在运行时将该值相乘,并对结果进行舍入以获得整数值。

好的,但由于doublefloat都表现得那样,为什么我会在其中一个案例中获得18,但17其他情况?我很困惑。

您的代码获取函数0.1f(浮点数)的结果,然后计算20 * (1.0 - 0.1f)这是一个双精度表达式,而20 * (1.0f - 0.1f)是一个浮点表达式。现在浮动版本略大于18.0并向下舍入到18,而双重表达式略小于18.0并向下舍入到17

如果您不确切地知道IEEE754二进制浮点数是如何从十进制数构造的,那么它几乎是随机的,如果它比您在代码中输入的十进制数稍微或略大一些。所以你不应该指望这一点。不要尝试通过将f附加到其中一个数字来解决此类问题并说“现在它可以正常工作,所以我将此f留在那里”,因为其他值的行为会再次不同。

为什么表达式的类型取决于此f的优先级?

这是因为C和C ++中的浮点字面值默认为double类型。如果你添加f,它就是一个浮点数。浮点epxression的结果是“更大”类型。 double表达式和整数的结果仍然是double表达式,int和float也是float。因此,表达式的结果是浮点数或双精度数。

好的,但我不想舍入到零。我想四舍五入到最接近的数字。

要解决此问题,请在将结果转换为整数之前将一半添加到结果中:

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()) + 0.5);

在C ++ 11中,有std::round()。在该标准的先前版本中,there was no such function to round to the nearest integer.(有关详细信息,请参阅注释。)

如果您没有std::round,可以自己编写。处理负数时要小心。转换为整数时,数字将截断(舍入为零),这意味着负值将向上舍入而不是向下舍入。因此,如果数字是负数,我们必须减去一半:

int round(double x) {
    return (x < 0.0) ? (x - .5) : (x + .5);
}

答案 1 :(得分:4)

1.0被解释为double,而不是1.0f,编译器将其视为float。

f后缀只是告诉编译器哪个是float,哪个是double。

顾名思义,double的精度是float的2倍。通常,double有15到16个十进制数字的精度,而float只有7。

这种精度损失可能导致截断错误更容易浮起

See MSDN (C++)

答案 2 :(得分:4)

使用double,即1.0时,发生这种情况的原因更准确。

尝试对结果进行舍入,这将导致转换后更精确的积分结果:

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()) + 0.5);

请注意,添加0.5并在其后截断int会导致结果四舍五入,因此当您的结果为17.999...时,它将变为18.499... },将被截断为18

相关问题