一个接近于零的浮点值是否会导致零除错误?

大家都知道你不应该直接比较花车,而是使用宽容:

float a,b; float epsilon = 1e-6f; bool equal = (fabs(ab) < epsilon); 

我想知道是否同样适用于比较一个值为零,然后在分区中使用它。

 float a, b; if (a != 0.0f) b = 1/a; // oops? 

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

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

当分母非零但非常接近于零(例如,低于正常)时,也可能得到无穷大(和一个溢出exception),但这也不是错误。 这只是浮点运作。

编辑:请注意,正如Eric在评论中指出的那样,这个答案假设了附录F的要求,附录F是C标准的一个可选部分,详细描述了浮点行为,并将其与IEEE标准的浮点标准alignment。 在没有IEEE算术的情况下,C没有定义浮点除零(事实上,所有浮点运算的结果都是实现定义的,可能被定义为完全无意义的,仍然符合C标准),所以如果你正在处理一个不符合IEEE浮点的奇怪的C实现,你将不得不查阅你正在用来回答这个问题的实现的文档。

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

某些C实现(以及其他一些计算环境)可能会以flush-underflow模式执行,尤其是在使用高性能选项的情况下。 在这种模式下,除法可以导致除以零的相同结果。 当使用向量(SIMD)指令时,冲洗下溢模式并不less见。

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

正确处理非正规数字(符合IEEE 754)要求在某些处理器中有额外的计算时间。 为了避免不必要的延迟,处理器可能有一个将非正规操作数转换为零的模式。 然后用一个非正规操作数分开一个数,然后产生相同的结果除以零,即使通常的结果是有限的。

正如其他答案中所指出的那样,在采用C标准的附录F的C实现中,除以零不是错误。 不是所有的实现。 在不这样做的实现中,您无法确定是否启用了浮点陷阱,特别是除零除外的陷阱,而没有关于您的环境的附加说明。

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

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

 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; 

这会产生以下输出:

 1e-300 1e+300 inf 1 

只有正好0.f的一个分区才会引起零分例外的分割。

然而,由一个非常小的数字划分可能会产生一个溢出exception – 结果是如此之大,以致于不再可以用浮点数表示。 师将返回无限。

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

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

您将不会收到除零错误,因为0.0f完全在IEEE浮点数中表示 。

这就是说,你可能仍然想要使用一些宽容 – 尽pipe这完全取决于你的应用程序。 如果“零”值是其他math的结果,可能会得到一个非常小的,非零的数字,这可能会导致您的分裂后意想不到的结果。 如果您想将“接近零”的数字视为零,那么宽容就是合适的。 但是,这完全取决于您的应用程序和目标。

如果您的编译器使用IEEE 754标准进行exception处理 ,那么除以0,并除以一个足够小的值以导致溢出,否则都会导致+/- infiniti的值。 这可能意味着您可能希望包含非常小的数字(这会导致您的平台溢出)的检查。 例如,在Windows上 , floatdouble都符合规范,这可能会导致非常小的除数来创build+/- infiniti,就像一个零值。

如果您的编译器/平台不遵循IEEE 754浮点标准,那么我相信结果是特定于平台的。