除以非零仍然可以创建 nan / infinity

Posted

技术标签:

【中文标题】除以非零仍然可以创建 nan / infinity【英文标题】:Can division by non-zero still create a nan / infinity 【发布时间】:2016-11-29 00:54:54 【问题描述】:

我有一个可能是零的数字。我除以那个数字,所以我想测试它是否为零以防止 NaN 和无穷大。由于除法内的舍入误差,我是否仍可能创建 NaN / 无穷大?

double x; // might be zero
double y;

if(x != 0) return y / x;

编辑

感谢您的回复。然后我会添加一些子问题。

1) 假设 x 和 y 都不是 NaN / +inf 或 -inf,导致 -inf / +inf 的除法会导致更多 CPU 周期或任何其他不需要的行为吗? (会不会崩溃?)

2) 有没有办法防止除法导致无穷大?使用偏移量等等。

【问题讨论】:

它们可能是无穷大甚至是纳米 如果y已经是NaN,那么结果也是NaN。 在某人已经发布了一个答案(在本例中为 3 个)之后添加更多问题并不是很好 - 这会使这些答案看起来不完整,尽管它们在编写时并不完整。 这就是为什么我写了一个很大的“编辑”。 :) 我知道这不好,但不知怎的,我错过了原始帖子的问题。 【参考方案1】:

除以非零仍然可以创建一个 nan / infinity

是的。

如果遵循 IEEE-754,则:

如果任一操作数为 NaN,则结果将为 NaN。 如果分子和分母都是无穷大,则结果为 NaN。 如果只有分子为无穷大,则结果为无穷大。 如果除以小分子(或大分子)溢出,结果可能是无穷大,具体取决于当前的舍入模式。

其他表示的规则可能不同。


2) 有没有办法防止除法导致无穷大

这应该可以大大防止这种情况发生:

#include <cfenv>
#include <cassert>
#include <cmath>
#include <limits>

// ...

static_assert(std::numeric_limits<decltype(x)>::is_iec559, "Unknown floating point standard.");
#pragma STDC FENV_ACCESS ON
int failed = std::fesetround(FE_TOWARDZERO);
assert(!failed);
if(x != 0 && std::isfinite(x) && std::isfinite(y))
    return y / x;
else
    throw std::invalid_argument("informative message");

某些编译器可能需要非默认选项来启用完全 IEEE 754 合规性(GCC 上的-frounding-math)。

【讨论】:

"取决于当前的舍入模式" 这很关键,请参阅fegetround/fesetround。如果你向零舍入,如果你除以有限非零,你永远不会得到一个 inf。 我觉得 fesetround 不应该在性能敏感的函数中调用,对吧? @ruhigbrauner 可能不是。你可能想把它移到任何热循环之外。【参考方案2】:

将一个非常小的数字除以一个非常大的数字,或者将两个非常大的数字相乘,可能会产生“无穷大”。将一个无穷大除以另一个无穷大将产生 NaN。例如,(1E300/1E-300)/(1E300/1E-300)(1E300*1E300)/(1E300*1E300) 都会产生 NaN。

【讨论】:

【参考方案3】:

是的,看看下面的代码

#include <iostream>
int main ()

    double x = 1, y = 2;
    while (y != 0) 
        std::cout << y << " " << x / y << std::endl;
        y /= 2;
    

在某个时刻你会得到:

8.9003e-308 1.12356e+307
4.45015e-308 2.24712e+307
2.22507e-308 4.49423e+307
1.11254e-308 8.98847e+307
5.56268e-309 inf
2.78134e-309 inf
1.39067e-309 inf
6.95336e-310 inf

【讨论】:

以上是关于除以非零仍然可以创建 nan / infinity的主要内容,如果未能解决你的问题,请参考以下文章

在java中的double和float类型数据相除为啥可以除以零

Infinity 和 NaN 的类型都有哪些?

你如何让 VB6 用 +infinity、-infinity 和 NaN 初始化双精度数?

无穷小怎么表示

java问题记录

Java中为啥整数除以0出现异常