除以非零仍然可以创建 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类型数据相除为啥可以除以零