在 C 浮点数中用 NaN 而不是 abort 的目的是什么?
What is the purpose of NaN instead of abort in C floating-point?
我从 GNU 科学图书馆得到了一个计算结果。看起来像这样
iter 0: A = 1.0000, lambda = 1.0000, b = 0.0000, cond(J) = inf, |f(x)| = 101.0200
iter 1: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = 92.8216, |f(x)| = -nan
iter 2: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 3: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 4: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 5: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 6: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 7: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 8: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 9: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 10: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 11: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 12: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 13: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 14: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 15: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 16: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 17: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 18: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 19: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 20: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
因此,您从第二次迭代中看到 cond(J) = nan, |f(x)| = -nan
。从语言设计的角度来看,默默地使用“nan”而不是中止的目的是什么?效率?
From the language design perspective, what is the purpose of having "nan" silently, rather than abort? Efficiency?
效率是原因之一。但另一个是C缺乏异常的概念。考虑以下。您正在使用无法访问源代码的预编译库。你只有头文件和目标文件,所以你有一个在头文件中声明并在目标文件中定义的函数double f(double arg)
。
不想程序崩溃怎么办?然后,如果 arg
的值使得 f
会 return NaN
,则您必须确保甚至不调用该函数。这确实非常棘手。特别是在这种情况下,您甚至无法访问源代码。
在Java中,计算NaN时不抛出异常。但如果是,你会做这样的事情:
try {
a = f(4.0);
} catch (NaNException) {
你会如何在 C 中做到这一点?当用整数除以零时,这实际上是 C 语言中的一个问题。这会在 C 中调用未定义的行为,没有机会恢复。
这个java代码:
try {
a = 100/d;
} catch (ArithmeticExpression) {
System.out.println("<d> must not be zero");
对应这个C代码:
if(d == 0) {
puts("<d> must not be zero");
} else {
a = 100/d;
请注意,您必须在 之前进行检查,这比事后进行检查要棘手得多。在这个简单的例子中,这是微不足道的。但是考虑一个稍微不那么简单的例子。想象一下,您有一个执行总共 20 个除法的函数。那么您可能需要 20 个 if 语句来解决这个问题。我们也有上面提到的情况,您无法访问源代码,只能完全相信创建库的人。
NaN means the involved calculation is meaningless or totally erroneous, right?
不是真的。代码可能完全没问题,错误可能是代码输入错误。这并不一定意味着程序需要崩溃。
而 C 中的错误处理基本上是由程序员进行适当的错误检查。
实际上,您可以使用 NAN
作为明确的错误指示符。示例:
double f(double arg) {
if(arg < 0)
return NAN;
...
}
但要小心,因为这行不通:
if(f(a) == NAN) {
改为使用:
if(isnan(f(a))) {
我从 GNU 科学图书馆得到了一个计算结果。看起来像这样
iter 0: A = 1.0000, lambda = 1.0000, b = 0.0000, cond(J) = inf, |f(x)| = 101.0200
iter 1: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = 92.8216, |f(x)| = -nan
iter 2: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 3: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 4: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 5: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 6: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 7: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 8: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 9: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 10: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 11: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 12: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 13: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 14: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 15: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 16: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 17: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 18: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 19: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
iter 20: A = 3.5110, lambda = -12.8820, b = 1.2364, cond(J) = nan, |f(x)| = -nan
因此,您从第二次迭代中看到 cond(J) = nan, |f(x)| = -nan
。从语言设计的角度来看,默默地使用“nan”而不是中止的目的是什么?效率?
From the language design perspective, what is the purpose of having "nan" silently, rather than abort? Efficiency?
效率是原因之一。但另一个是C缺乏异常的概念。考虑以下。您正在使用无法访问源代码的预编译库。你只有头文件和目标文件,所以你有一个在头文件中声明并在目标文件中定义的函数double f(double arg)
。
不想程序崩溃怎么办?然后,如果 arg
的值使得 f
会 return NaN
,则您必须确保甚至不调用该函数。这确实非常棘手。特别是在这种情况下,您甚至无法访问源代码。
在Java中,计算NaN时不抛出异常。但如果是,你会做这样的事情:
try {
a = f(4.0);
} catch (NaNException) {
你会如何在 C 中做到这一点?当用整数除以零时,这实际上是 C 语言中的一个问题。这会在 C 中调用未定义的行为,没有机会恢复。
这个java代码:
try {
a = 100/d;
} catch (ArithmeticExpression) {
System.out.println("<d> must not be zero");
对应这个C代码:
if(d == 0) {
puts("<d> must not be zero");
} else {
a = 100/d;
请注意,您必须在 之前进行检查,这比事后进行检查要棘手得多。在这个简单的例子中,这是微不足道的。但是考虑一个稍微不那么简单的例子。想象一下,您有一个执行总共 20 个除法的函数。那么您可能需要 20 个 if 语句来解决这个问题。我们也有上面提到的情况,您无法访问源代码,只能完全相信创建库的人。
NaN means the involved calculation is meaningless or totally erroneous, right?
不是真的。代码可能完全没问题,错误可能是代码输入错误。这并不一定意味着程序需要崩溃。
而 C 中的错误处理基本上是由程序员进行适当的错误检查。
实际上,您可以使用 NAN
作为明确的错误指示符。示例:
double f(double arg) {
if(arg < 0)
return NAN;
...
}
但要小心,因为这行不通:
if(f(a) == NAN) {
改为使用:
if(isnan(f(a))) {