将cast float与零比较时的奇怪行为

noc*_*lux 1 c++ floating-point comparison

我目前正在尝试深入了解浮点表示,所以我玩了一下.在这样做的时候,我偶然发现了一些奇怪的行为; 我无法确定正在发生的事情,我会非常感激一些见解.抱歉,如果这已经得到解答,我发现谷歌很难!

#include <iostream>
#include <cmath>
using namespace std;

int main(){

  float minVal = pow(2,-149); // set to smallest float possible
  
  float nextCheck = static_cast<float>(minVal/2.0f); // divide by two
  bool isZero = (static_cast<float>(minVal/2.0f) == 0.0f); // this evaluates to false
  bool isZero2 = (nextCheck == 0.0f); // this evaluates to true
  cout << nextCheck << " " << isZero << " " << isZero2 << endl;
  // this outputs 0 0 1
  
  return 0;

}
Run Code Online (Sandbox Code Playgroud)

基本上发生的事情是:

  • 我将minVal设置为可以使用单精度表示的最小浮点数
  • 除以2应该得到0 - 我们是最小的
  • 实际上,isZero2确实返回true,但是isZero返回false.

发生了什么 - 我会认为它们是相同的?编译器是否试图变得聪明,说除了任何数字都不可能产生零?

谢谢你的帮助!

Pas*_*uoq 5

原因isZero并且isZero2可以评估为不同的值,并且isZero可能是错误的,是允许C++编译器实现中间浮点运算,其精度高于表达式所指示的类型,但必须在赋值时删除额外的精度. .

通常,在为387历史FPU生成代码时,生成的指令适用于80位扩展精度类型,或者,如果FPU设置为53位有效数字(例如,在Windows上),则为奇怪的浮点数输入53位有效数字和15位指数.

无论哪种方式,minVal/2.0f都要进行精确评估,因为指数范围允许表示它,但是将其指定为将其nextCheck舍入为零.

如果您正在使用GCC,则还有一个-fexcess-precision=standard尚未针对C++前端实现的问题,这意味着g ++生成的代码并未完全实现标准建议的内容.