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

hax*_*por 4 c++ floating-point double floating-accuracy

我有以下代码行.

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

让我们说hero->getDefensePercent()回报0.1.所以计算是

20 * (1.0 - 0.1)  =  20 * (0.9)  =  18
Run Code Online (Sandbox Code Playgroud)

每当我尝试使用以下代码时(无f附加1.0)

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()));
Run Code Online (Sandbox Code Playgroud)

17岁了.

但是对于以下代码(f后面附后1.0)

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0f - hero->getDefensePercent()));
Run Code Online (Sandbox Code Playgroud)

18岁了.

这是怎么回事?是f显著要不惜任何虽然hero->getDefensePercent()已在浮动?

lee*_*mes 11

这是怎么回事?18两种情况下为什么不是整数结果?

问题是浮点表达式的结果在转换为整数值时(在两种情况下)都舍入为零.

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

好了,但由于双方doublefloat那样做,为什么会18在这两种情况之一,但17在另一种情况?我糊涂了.

您的代码获取函数的结果0.1f(浮点数),然后计算20 * (1.0 - 0.1f)哪个是双表达式,而20 * (1.0f - 0.1f)浮点表达式.现在浮动版本恰好略大于18.0并且向下舍入到18,而double表达式稍微小于18.0并向下舍入到17.

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

为什么要依赖表达式的类型f呢?

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

好的,但我不想舍入到零.我想要舍入到最接近的数字.

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

hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()) + 0.5);
Run Code Online (Sandbox Code Playgroud)

在C++ 11中,就是std::round()这样.在该标准的先前版本中,没有这样的函数可以舍入到最接近的整数.(详情请见评论.)

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

int round(double x) {
    return (x < 0.0) ? (x - .5) : (x + .5);
}
Run Code Online (Sandbox Code Playgroud)

  • 您不必添加"0.5",也不必编写自己的函数.只需使用`std :: round()`(在`<cmath>`中).一般来说,我建议总是使用其中一个标准函数(`round`,`floor`,`ciel`或`trunc`); 这使你的意图清晰(并让你思考你真正想做的事情). (3认同)