GNU awk中涉及NaN的惊人数字比较结果(gawk)
使用awk/gawk,我需要执行涉及NaN浮点值的数值比较。尽管gawk似乎已将我的用户输入正确地转换为数值NaN(即,不是字符串“NaN”),但与运算符“”执行的比较结果与我的预期不符 期望值: 比较,如GNU awk中涉及NaN的惊人数字比较结果(gawk),awk,floating-point,nan,Awk,Floating Point,Nan,使用awk/gawk,我需要执行涉及NaN浮点值的数值比较。尽管gawk似乎已将我的用户输入正确地转换为数值NaN(即,不是字符串“NaN”),但与运算符“”执行的比较结果与我的预期不符 期望值: 比较,如x>y,或xnumbr)) 返回-1; // } 字符串转换不是这里唯一的问题。否则awk'开始{printf“%s\n”,(“+nan”+0)@oliv更新了答案。答案是在源代码中找到的。@oliv,现在我确信AWK中有一个错误这一切都是非常不幸的。如此简单的技术问题,再加上遗留的和相互冲
x>y
,或x
,其中x为NaN,y为浮点值(包括NaN和+/-无穷大),应计算为false。[需要引用IEEE文件(但有表格)]
实际结果:
function isnum(x) { return x+0 == x }
function isnan(x) { return (x+0 == "+nan"+0) }
function isinf(x) { return ! isnan(x) && isnan(x-x) }
BEGIN{inf=log(0.0);nan=sqrt(-1.0);one=1;foo="nano";
print "INF", inf , isnum(inf) , isnan(inf) , isinf(inf)
print "INF", -inf , isnum(-inf) , isnan(-inf) , isinf(-inf)
print "INF", "+inf", isnum("+inf"), isnan("+inf"), isinf("+inf")
print "INF", "-inf", isnum("-inf"), isnan("-inf"), isinf("-inf")
print "NAN", nan , isnum(nan) , isnan(nan) , isinf(nan)
print "NAN", -nan , isnum(-nan) , isnan(-nan) , isinf(-nan)
print "NAN", "+nan", isnum("+nan"), isnan("+nan"), isinf("+nan")
print "NAN", "-nan", isnum("-nan"), isnan("-nan"), isinf("-nan")
print "ONE", one , isnum(one) , isnan(one) , isinf(one)
print "FOO", foo , isnum(foo) , isnan(foo) , isinf(foo)
}
NaN<2.0==0,但NaN>2.0==1
下面的代码片段获取第一个字段并将0
添加到其中,以强制转换为整数(如所述)。然后它使用printf显示变量和表达式的类型(我的特定版本的gawk没有typeof()
)
$echo-e“+nan\n-nan\nfoo\nnany”| awk\
“{x=($1+0);printf”%s:float=%f str=%s x2=%f\n“,$1,x,x,(x2.0);}”
+nan:float=nan str=nan x2==1.000000
-nan:float=nan str=nan x2==1.000000
foo:float=0.000000str=0 x2=0.000000
保姆:浮动=0.000000str=0 x2==0.000000
$echo-e“+nan\n-nan\n foo\nnany”| awk-posix\
“{x=($1+0);printf”%s:float=%f str=%s x2=%f\n“,$1,x,x,(x2.0);}”
+nan:float=nan str=nan x2==1.000000
-nan:float=nan str=nan x2==1.000000
foo:float=0.000000str=0 x2=0.000000
保姆:float=nan str=nan x2==1.000000
运行GNU Awk 4.1.3,API:1.1
是否有其他方式/选项使NAN正确传播?
我读了那页关于南的文章,我想我说的没错。我有一种感觉,NaN可能并没有很好地融入awk。例如,我找不到一种可靠的方法来测试一个值是否为NaN(通过printf除外)。POSIX必须说什么? 首先,POSIX允许但不要求awk支持
NaN
或Inf
值。从:
awk的历史实现不支持数字字符串中的浮点无限和NAN;e、 例如,“-INF”
和“NaN”
。但是,使用or函数进行转换的实现如果使用ISO/IEC 9899:1999标准版本的函数而不是ISO/IEC 9899:1990标准版本,则会获得对这些值的支持。由于疏忽,本标准2001年至2004年版本不允许支持无穷大和NaN,但在本修订版中,支持是允许的(但不是必需的)。这是对awk程序行为的无声改变;例如,在POSIX语言环境中,表达式:
("-INF" + 0 < 0)
备注:当使用--posix
时,GNU awk可能会识别字符串“nan”
和“inf”
以及其他字符串,如“infinity”
或完全意外的字符串“nano”
或“info”
。后者可能是-当不使用--posix
时-符号最重要且仅识别字符串“+nan”、“-nan”、“+inf”和“-inf”的主要原因
GNU awk如何对这些神奇的IEEE数字进行排序?
在深入研究GNU awk的源代码时,我们发现例程有以下注释:
这就解释了OP最初的问题,为什么NaN没有遵循IEEE比较,因此(“+NaN”+02)
是1(真)
。(备注:我们在字符串中添加了零以确保数字转换)
这可以用以下代码(no--posix
)来演示:
输出以下顺序:
-inf -1 -0.0 0.0 1 1.0 1.0 2.0 +inf +nan -nan
-nan +nan +inf 2.0 1.0 1.0 1 0.0 -0.0 -1 -inf
如果NaN
遵循IEEE惯例,则它应始终出现在列表的开头,而不考虑顺序,但情况显然并非如此。使用--posix
时也是如此:
function arr_sort(arr, x, y, z) {
for (x in arr) { y = arr[x]; z = x - 1
# force numeric comp
while (z && arr[z]+0 > y+0) { arr[z + 1] = arr[z]; z-- }
arr[z + 1] = y
}
}
BEGIN { s = "1.0 +nan 0.0 -1 +inf -0.0 1 1.0 -nan -inf 2.0"
s = s" inf nan info -infinity"; split(s, a)
arr_sort(a)
for (i in a) printf a[i] OFS; printf "\n"
}
-inf -infinity -1 0.0 -0.0 1.0 1 1.0 2.0 +inf inf info +nan -nan nan
请注意,字符串“info”被视为一个无穷大,而在没有--posix
的情况下,它将被转换为零
(同上,表示“inf”
,“nan”
,…)
如何处理(“+nan”<2)
和(“+nan”+0<2)
?
在第一种情况下,将执行纯字符串比较,而在第二种情况下,将字符串强制为数字并执行数字比较。这类似于(“2.0”==2)
和(“2.0”+0==2)
。第一个返回false,第二个返回true。这种行为的原因是,在第一种情况下,awk只知道“2.0”是字符串,它不检查其内容,因此它将2
转换为字符串
BEGIN { print ("-nan" < 2) , ("-nan" > 2) , ("+nan" < 2) , ("+nan" > 2)
print ("-nan"+0 < 2), ("-nan"+0 > 2), ("+nan"+0 < 2), ("+nan"+0> 2)
print ("-nan"+0 ) , ("-nan"+0) , ("+nan"+0) , ("+nan"+0) }
1 0 1 0
0 1 0 1
nan nan nan nan
这将返回:
INF -inf 1 0 1
INF inf 1 0 1
INF +inf 1 0 1
INF -inf 1 0 1
NAN -nan 1 1 0
NAN nan 1 1 0
NAN +nan 1 1 0
NAN -nan 1 1 0
ONE 1 1 0 0
FOO nano 0 0 0
我们可以确信,在调查的源代码时,isnan(x)
函数按预期工作(添加了一些注释以解释):
int cmp\u awknums(常数节点*t1,常数节点*t2)
{
//isnan是C版本的
//这确保了所有NAN都是平等的
如果(isnan(t1->numbr))
返回!isnan(t2->numbr);
//这确保了所有NAN都比任何其他数字都大
如果(isnan(t2->numbr))
返回-1;
//
}
字符串转换不是这里唯一的问题。否则awk'开始{printf“%s\n”,(“+nan”+0)@oliv更新了答案。答案是在源代码中找到的。@oliv,现在我确信AWK中有一个错误这一切都是非常不幸的。如此简单的技术问题,再加上遗留的和相互冲突的标准。我想知道对包含NaN的数组进行排序时出现的差异是否是由于排序本身没有得到保证造成的'“稳定”。您似乎对awk内部结构非常熟悉,您是否愿意建议,在没有可靠的NaN安全比较运算符的情况下,如何在使用典型运算符之前,首先安全地确定值是否为数值NaN
,即函数isNaN(x){…}如果isNaN(x)&&x>2.0<
function arr_sort(arr, x, y, z) {
for (x in arr) { y = arr[x]; z = x - 1
# force numeric comp
while (z && arr[z]+0 > y+0) { arr[z + 1] = arr[z]; z-- }
arr[z + 1] = y
}
}
BEGIN { s = "1.0 +nan 0.0 -1 +inf -0.0 1 1.0 -nan -inf 2.0"
s = s" inf nan info -infinity"; split(s, a)
arr_sort(a)
for (i in a) printf a[i] OFS; printf "\n"
}
-inf -infinity -1 0.0 -0.0 1.0 1 1.0 2.0 +inf inf info +nan -nan nan
BEGIN { print ("-nan" < 2) , ("-nan" > 2) , ("+nan" < 2) , ("+nan" > 2)
print ("-nan"+0 < 2), ("-nan"+0 > 2), ("+nan"+0 < 2), ("+nan"+0> 2)
print ("-nan"+0 ) , ("-nan"+0) , ("+nan"+0) , ("+nan"+0) }
1 0 1 0
0 1 0 1
nan nan nan nan
function isnum(x) { return x+0 == x }
function isnan(x) { return (x+0 == "+nan"+0) }
function isinf(x) { return ! isnan(x) && isnan(x-x) }
BEGIN{inf=log(0.0);nan=sqrt(-1.0);one=1;foo="nano";
print "INF", inf , isnum(inf) , isnan(inf) , isinf(inf)
print "INF", -inf , isnum(-inf) , isnan(-inf) , isinf(-inf)
print "INF", "+inf", isnum("+inf"), isnan("+inf"), isinf("+inf")
print "INF", "-inf", isnum("-inf"), isnan("-inf"), isinf("-inf")
print "NAN", nan , isnum(nan) , isnan(nan) , isinf(nan)
print "NAN", -nan , isnum(-nan) , isnan(-nan) , isinf(-nan)
print "NAN", "+nan", isnum("+nan"), isnan("+nan"), isinf("+nan")
print "NAN", "-nan", isnum("-nan"), isnan("-nan"), isinf("-nan")
print "ONE", one , isnum(one) , isnan(one) , isinf(one)
print "FOO", foo , isnum(foo) , isnan(foo) , isinf(foo)
}
INF -inf 1 0 1
INF inf 1 0 1
INF +inf 1 0 1
INF -inf 1 0 1
NAN -nan 1 1 0
NAN nan 1 1 0
NAN +nan 1 1 0
NAN -nan 1 1 0
ONE 1 1 0 0
FOO nano 0 0 0