接近零浮点值是否会导致被零除错误?

neu*_*rte 59 c c++ floating-point divide-by-zero

每个人都知道你不应该直接比较花车,而是使用公差:

float a,b;
float epsilon = 1e-6f;
bool equal = (fabs(a-b) < epsilon);
Run Code Online (Sandbox Code Playgroud)

我想知道在分割中使用它之前是否同样适用于将值比较为零.

float a, b;
if (a != 0.0f) b = 1/a; // oops?
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我还需要与epsilon进行比较吗?

R..*_*R.. 67

浮点除以零不是错误.它会在支持浮点异常的实现上引发一个浮点异常(除非你主动检查它们,这是一个无操作),并且具有明确定义的结果:正或负无穷大(如果分子非零),或者NAN(如果分子为零).

当分母非零但非常接近零(例如,低于正常)时,也可能得到无穷大(和溢出异常),但同样这不是错误.这就是浮点的工作原理.

编辑:请注意,正如Eric在评论中指出的那样,这个答案假定了附件F的要求,附件F是C标准的可选部分,详细说明浮点行为并将其与浮点的IEEE标准对齐.在没有IEEE算法的情况下,C不会将浮点除法定义为零(实际上,所有浮点运算的结果都是实现定义的,可能被定义为完全无意义且仍然符合C标准),因此,你正在处理一个不尊重IEEE浮点数的古怪C实现,你将不得不参考你用来回答这个问题的实现的文档.

  • 让我强调:浮点异常**不是**C++异常.你不能在它周围放置一个try-catch块.除非你去寻找它,否则你不会看到它,这需要一些专业知识.正如@R ..所说,除非你知道如何找到它,否则这是一个无操作.除非你是专家,否则你不想这样做. (24认同)
  • C标准没有定义默认的浮点环境,除非实现采用附件F.没有附件F,默认情况下可以启用陷阱.在编译时使用高性能选项可能会启用不符合C标准或IEEE 754的模式.除以零的一揽子声明不是错误是没有根据的. (5认同)
  • 没有附件F,对浮点行为基本没有要求.`2.0 + 2.0 == 5.0`可以是真的.在没有假设附件F的情况下谈论C中的浮点是没有意义的. (4认同)
  • 你能否引用C标准中暗示"2.0 + 2.0!= 5.0"的部分?我不知道有任何这样的要求. (3认同)

Eri*_*hil 24

是的,在某些情况下,除以小数可能会产生与除以零相同的效果,包括陷阱.

某些C实现(以及一些其他计算环境)可以在刷新 - 下溢模式下执行,尤其是在使用高性能选项的情况下.在这种模式下,除以非正规可以得到与除以零相同的结果.当使用向量(SIMD)指令时,刷新下溢模式并不罕见.

非正规数是具有浮点格式中最小指数的数字,它们非常小,以至于有效数的隐含位为0而不是1.对于IEEE 754,单精度,这是非零数字,幅度小于2 -126.对于双精度,它是非零数字,幅度小于2 -1022.

正确处理非正规数字(根据IEEE 754)在某些处理器中需要额外的计算时间.为了在不需要时避免这种延迟,处理器可以具有将非正规操作数转换为零的模式.将数字除以非正规操作数将产生与除以零相同的结果,即使通常的结果是有限的.

如其他答案所述,除以零不是采用C标准附件F的C实现中的错误.并非所有实现都可以.在没有实现浮点陷阱的实现中,您无法确定是否启用了浮点陷阱,特别是除零异常的陷阱,而没有关于您的环境的其他规范.

根据您的具体情况,您可能还必须防止应用程序中的其他代码改变浮点环境.

  • 完全挑剔点:2008年版的IEEE-754将"非正常"改为"次正常".术语的这种变化不会改变上述任何一点. (2认同)

das*_*ght 9

要回答帖子标题中的问题,除以非常小的数字不会导致除数为零,但可能会导致结果变为无穷大:

double x = 1E-300;
cout << x << endl;
double y = 1E300;
cout << y << endl;
double z = y / x;
cout << z << endl;
cout << (z == std::numeric_limits<double>::infinity()) << endl;
Run Code Online (Sandbox Code Playgroud)

会产生以下输出:

1e-300
1e+300
inf
1
Run Code Online (Sandbox Code Playgroud)


Jen*_*gby 7

正好只有0.f的除法会将除数除以零.

但是,除以非常小的数字可能会产生溢出异常 - 结果非常大,以至于它不再能由浮点数表示.分裂将返回无限.

无穷大的浮点表示可用于计算,因此如果您的实现的其余部分可以处理它,则可能不需要检查它.