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

时间:2012-08-24 18:03:48

标签: c++ c floating-point divide-by-zero

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

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

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

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

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

5 个答案:

答案 0 :(得分:67)

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

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

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

答案 1 :(得分:24)

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

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

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

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

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

根据您的具体情况,您可能还需要防范应用程序中的其他代码改变浮点环境。

答案 2 :(得分: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;

produces以下输出:

1e-300
1e+300
inf
1

答案 3 :(得分:7)

只有正好0.f的除法才会将除法异常提高。

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

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

答案 4 :(得分:3)

  

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

您将不会收到除以零错误,因为0.0f完全以IEEE float表示。

话虽如此,你可能仍然想要使用一些容忍度 - 尽管这完全取决于你的应用程序。如果“零”值是其他数学运算的结果,则可能会得到一个非常小的非零数字,这可能会导致分割后的意外结果。如果要将“接近零”数字视为零,则容差是合适的。但这完全取决于您的应用和目标。

如果你的编译器正在使用IEEE 754 standards for exception handling,那么除以零,以及除以一个小到足以导致溢出的值的除法,都会产生+/- infiniti的值。这可能意味着您可能希望将检查包含在非常小的数字中(这会导致您的平台出现溢出)。例如,在Windows上,floatdouble都符合规范,这可能导致非常小的除数创建+/-英菲尼迪,就像零值一样。

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