C++ 浮点计算与双精度浮点计算得出的结果不同
我有下面一行代码C++ 浮点计算与双精度浮点计算得出的结果不同,c++,floating-point,double,floating-accuracy,C++,Floating Point,Double,Floating Accuracy,我有下面一行代码 hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent())); void onBeingHit(int decHP)方法接受整数并更新健康点 float getDefensePercent()方法是返回英雄防御百分比的getter方法 敌方攻击点是一个宏常量因子,定义为定义敌方攻击点20 假设hero->getDefensePercent()返回0.1。因此,计算是正确的 20
hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()));
方法接受整数并更新健康点void onBeingHit(int decHP)
方法是返回英雄防御百分比的getter方法float getDefensePercent()
是一个宏常量因子,定义为敌方攻击点
定义敌方攻击点20
hero->getDefensePercent()
返回0.1
。因此,计算是正确的
20 * (1.0 - 0.1) = 20 * (0.9) = 18
每当我尝试使用以下代码时(无f
追加1.0
)
我得了17分
但是对于以下代码(f
附加在1.0
之后)
我得了18分
发生什么事了?尽管
hero->getDefensePercent()
已处于浮点状态,但f
是否有意义?1.0被解释为双精度,而编译器将其视为浮点
f后缀只是告诉编译器哪个是浮点,哪个是双精度
顾名思义,double的精度是float的2倍。一般来说,双精度浮点型只有15到16位小数精度,而浮点型只有7位小数精度
这种精度损失可能导致截断错误更容易浮起
发生这种情况的原因是使用
double
时的结果更精确,即1.0
尝试对结果进行四舍五入,这将在转换后得到更精确的积分结果:
hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()) + 0.5);
请注意,添加
0.5
并立即截断为int
将导致结果四舍五入,因此当您的结果为17.999…
时,它将变成18.499…
,这将被截断为18
发生了什么事?为什么两种情况下的整数结果都不是18
?
问题在于,当转换为整数值时(在这两种情况下),浮点表达式的结果向零舍入
0.1
不能精确表示为浮点值(在这两种情况下)。编译器将转换为二进制IEEE754浮点数,并决定是向上舍入还是向下舍入为可表示的值。然后,处理器在运行时将该值相乘,并对结果进行四舍五入以获得整数值
好,但是既然double
和float
都是这样,为什么在这两种情况中的一种情况下我得到18
,而在另一种情况下得到17
?我很困惑。
您的代码获取函数的结果,0.1f
(浮点),然后计算20*(1.0-0.1f)
,这是一个双表达式,而20*(1.0f-0.1f)
是一个浮点表达式。现在,浮点版本恰好略大于18.0
,并向下舍入到18
,而双精度表达式略小于18.0
,并向下舍入到17
如果您不知道IEEE754二进制浮点数是如何由十进制数构造而成的,那么如果IEEE754二进制浮点数略小于或略大于您在代码中输入的十进制数,则它几乎是随机的。所以你不应该指望这个。不要试图通过在其中一个数字后面添加f
,然后说“现在它可以工作了,所以我把这个f
放在那里”来解决这样的问题,因为另一个值的行为又不同了
为什么表达式的类型取决于此f
?
这是因为C和C++中的浮点文字是默认的类型<代码>双< />代码。如果添加
f
,则它是一个浮点。浮点表达式的结果是“更大”类型。双精度表达式和整数的结果仍然是双精度表达式,int和float的结果将是float。所以表达式的结果是浮点或双精度
好的,但我不想四舍五入为零。我想四舍五入到最近的数字。
若要解决此问题,请在将结果转换为整数之前将其添加一半:
hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()) + 0.5);
在C++11中,有一种方法可以实现这一点。在本标准以前的版本中,(有关详细信息,请参见注释。)
如果没有std::round
,可以自己编写。处理负数时要小心。转换为整数时,数字将被截断(向零舍入),这意味着负值将向上舍入,而不是向下舍入。因此,如果数字为负数,我们必须减去一半:
int round(double x) {
return (x < 0.0) ? (x - .5) : (x + .5);
}
int轮(双x){
回报率(x<0.0)-(x-.5):(x+.5);
}
无法准确保存.9
或.1
(使用浮点数据类型)。可能双精度较低会导致类似18.00x
的结果,而不是17.999xxx
。请注意,<代码>浮点> int 总是被下移。不太了解C++,但是我希望您的文字被解释为“代码>双< /代码>,从而迫使计算在<代码>双< /代码> s而不是<代码>浮点< /COD> > S.<代码> SunyyAthAkcPoint < /Co> >是整数。由于使用整数数学,您会遇到舍入错误。感谢您的深入解释。您不必添加0.5
,也不必编写自己的函数。只需使用std::round()
(在
中)。一般来说,我建议始终使用标准功能之一(round
、floor
、ciel
或trunc
);这让你的意图变得清晰(并让你思考你真正想要做什么)。@JamesKanze它只是C++11。我对答案进行了适当的编辑,谢谢。@leems它也是C99(和Posix),因此在大多数C++03甚至C++98实现中都存在。微软可能是个例外
hero->onBeingHit(ENEMY_ATTACK_POINT * (1.0 - hero->getDefensePercent()) + 0.5);
int round(double x) {
return (x < 0.0) ? (x - .5) : (x + .5);
}