代码之家  ›  专栏  ›  技术社区  ›  neuviemeporte

接近零的浮动值会导致被零除的错误吗?

  •  65
  • neuviemeporte  · 技术社区  · 13 年前

    每个人都知道你不应该直接比较浮动,而是应该使用容差:

    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 回复  |  直到 12 年前
        1
  •  70
  •   R.. GitHub STOP HELPING ICE    13 年前

    浮点除以零不是错误。它在支持浮点异常的实现上引发浮点异常(除非您正在积极检查它们,否则这是一个无运算),并具有定义明确的结果:正或负无穷大(如果分子为非零),或NAN(如果分子是零)。

    当分母为非零但非常接近零(例如,次正规)时,也有可能得到无穷大(以及溢出异常),但这不是错误。这就是浮点运算的原理。

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

        2
  •  26
  •   Eric Postpischil    4 年前

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

    一些C实现(以及一些其他计算环境)可能以刷新下溢模式执行,特别是在使用高性能选项的情况下。在这种模式下,除以一个次法线可以得到与除以零相同的结果。当使用矢量(SIMD)指令时,刷新下溢模式并不少见。

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

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

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

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

        3
  •  9
  •   Sergey Kalinichenko    13 年前

    为了回答你帖子标题中的问题,除以一个很小的数字不会导致除以零,但它可能会导致结果变成无穷大:

    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
    
        4
  •  8
  •   Jens Agby    13 年前

    只有精确除以0.f才会引发除以零的异常。

    然而,除以一个非常小的数字可能会产生溢出异常——结果太大,无法再用浮点表示。除法将返回无穷大。

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

        5
  •  3
  •   Reed Copsey    13 年前

    在这种情况下,我是否也需要与epsilon进行比较?

    您永远不会收到被零除的错误,因为 0.0f IEEE float

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

    如果编译器正在使用 IEEE 754 standards for exception handling ,然后除以零,以及除以一个小到足以导致溢出的值,都会产生+/-无穷大的值。这可能意味着你可能想要包括对非常小的数字的检查(这会导致你的平台上的溢出)。例如,在 Windows , float double 两者都符合规范,这可能会导致一个非常小的除数产生+/-infiniti,就像零值一样。

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