首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >gawk中涉及NaN的惊人数值比较结果(gawk)

gawk中涉及NaN的惊人数值比较结果(gawk)
EN

Stack Overflow用户
提问于 2018-06-27 07:24:51
回答 2查看 287关注 0票数 1

使用awk/gawk,我需要执行涉及NaN浮点值的数值比较。尽管gawk似乎正确地将我的用户输入转换为一个数值NaN (即不是字符串"NaN"),但是与操作符'<‘或'>’的比较结果并不符合我的预期。

期望

比较,例如x > y,或x < y,其中x是NaN,y是浮点值(包括NaN和+/-Infinity),应该计算为false。[引用IEEE所需的文档(但维基百科NaN有表)]。

实际结果:

NaN < 2.0 == 0,但NaN > 2.0 == 1

下面的代码段接受第一个字段,并将0添加到其中强制将其转换为整数(如描述的在gnu awk手册中)。然后使用printf来显示变量和表达式的类型(我的gawk的特定版本没有typeof())。

代码语言:javascript
复制
$ echo -e "+nan\n-nan\nfoo\nnanny" | awk \
'{x=($1+0); printf "%s: float=%f str=%s x<2==%f x>2==%f\n",$1,x,x,(x<2.0),(x>2.0);}'

+nan: float=nan str=nan x<2==0.000000 x>2==1.000000
-nan: float=nan str=nan x<2==0.000000 x>2==1.000000
foo: float=0.000000 str=0 x<2==1.000000 x>2==0.000000
nanny: float=0.000000 str=0 x<2==1.000000 x>2==0.000000

$ echo -e "+nan\n-nan\nfoo\nnanny" | awk --posix \
'{x=($1+0); printf "%s: float=%f str=%s x<2==%f x>2==%f\n",$1,x,x,(x<2.0),(x>2.0);}'           

+nan: float=nan str=nan x<2==0.000000 x>2==1.000000
-nan: float=nan str=nan x<2==0.000000 x>2==1.000000
foo: float=0.000000 str=0 x<2==1.000000 x>2==0.000000
nanny: float=nan str=nan x<2==0.000000 x>2==1.000000

运行GNU Awk 4.1.3,API: 1.1

是否有不同的方式/选项让NaNs正确地传播?我在标准与实践上读到了关于NaN的那一页,我认为我做得很对。我觉得NaN可能不是很好的烘焙成awk。例如,我找不到一种可靠的方法来测试某个值是否是NaN (除了通过printf)。

EN

回答 2

Stack Overflow用户

发布于 2018-06-27 08:07:29

POSIX必须说什么?首先允许,但不要求awk支持NaNInf值。来自美国IEEE Std 1003.1-2017年POSIX标准

awk的历史实现不支持数字字符串中的浮点无穷大和NaNs,例如"-INF""NaN"。但是,使用atof()strtod()函数进行转换的实现如果使用该函数的ISO/IEC 9899:1999标准版本而不是ISO/IEC 9899:1990标准版本,则会获得对这些值的支持。由于疏忽,该标准的2001年至2004年版本不允许支持无穷大和NaNs,,但是在此修订版中,支持是允许的(但不是必需的)。这是对awk程序行为的无声更改;例如,在POSIX语言环境中,表达式: ("-INF“+0< 0) 以前有值0,因为"-INF"转换为0,但现在可能有值0"-INF"

GNU如何处理这些神奇的IEEE数字? GNU awk手册声明:

  • 在不使用--posix的情况下,gawk专门解释四个字符串值"+inf""-inf""+nan“和"-nan",从而产生相应的特殊数字值。
  • 有了--posix命令行选项,gawk就变成了“不要碰”。字符串值直接传递给系统库的strtod()函数,如果它成功地返回一个数值,这就是所使用的。根据定义,结果不能跨不同系统移植。

因此,简而言之,GNU -没有--posix选项--只有才能成功地将字符串"+nan“、"-nan”、"+inf“和"-inf”转换为浮点表示(参见函数is_ieee_magic_val)。

令人惊讶的是,它没有转换"nan""inf",特别是因为"+nan"+0的字符串转换是一个无符号的"nan"

代码语言:javascript
复制
$ gawk 'BEGIN{print "+nan"+0, "nan"+0}'
nan 0

注意:当使用--posix时,GNU可能识别字符串"nan""inf"以及其他字符串,如"infinity"或完全出乎意料的"nano""info"。后者可能是--当不使用--posix时--符号非常重要的主要原因,只有字符串"+nan“、"-nan”、"+inf“和"-inf”才被识别。

GNU是如何对这些神奇的IEEE号码进行排序的?

在深入了解GNU的源代码时,我们发现了对常规cmp_awknums的以下注释

/* *此例程还用于排序数字数组索引或值。*为了排序的目的,NaN被认为大于*任何其他值,并且所有NaN值都被认为是等价的和相等的。*这不符合IEEE标准,但符合w.r.t。NaN *在awk级别上的比较是一个不同的问题,需要在解释器中分别为每个操作码处理。*/

这解释了OP最初的问题,为什么NaN没有遵循IEEE的比较,因此("+nan"+0<2)0 (false)("+nan"+0>2)1 (true)。(注意:我们在字符串中添加了一个零,以确保数字转换)

这可以用以下代码来演示(没有--posix):

代码语言:javascript
复制
BEGIN { s = "1.0 +nan 0.0 -1 +inf -0.0 1 1.0 -nan -inf 2.0"; split(s, a)
        PROCINFO["sorted_in"] = "@val_num_asc"
        for (i in a) printf a[i] OFS; printf "\n"
        PROCINFO["sorted_in"] = "@val_num_desc"
        for (i in a) printf a[i] OFS; printf "\n"
      }

输出下列命令:

代码语言:javascript
复制
-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时也是相同的

代码语言:javascript
复制
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,它将被转换为ZERO (dito表示"inf""nan",.)

("+nan" < 2) ("+nan"+0 < 2)**?**的交易是什么?

在第一种情况下,将进行纯字符串比较,而在第二种情况下,将字符串强制为数字,并进行数字比较。这类似于("2.0" == 2)("2.0"+0 == 2)。其中第一个返回false,第二个返回true。造成这种行为的原因是,在第一种情况下,awk只知道"2.0“是一个字符串,它不检查其内容,因此它将2转换为字符串。

代码语言:javascript
复制
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 nan**:**或

代码语言:javascript
复制
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)
}

这将返回:

代码语言:javascript
复制
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)的源代码时,cmp_awknums函数可以正常工作(添加了一些注释来解释):

代码语言:javascript
复制
int cmp_awknums(const NODE *t1, const NODE *t2)
{
    // isnan is here the C version
    // this ensures that all NANs are equal
    if (isnan(t1->numbr))
        return ! isnan(t2->numbr);
    // this ensures that all NANs are bigger than any other number
    if (isnan(t2->numbr))
        return -1;
    // <snip>
}
票数 3
EN

Stack Overflow用户

发布于 2022-08-31 09:09:38

下面是我能想到的最简洁的方法(我可以想到)来获得4个特殊值:

gawk 'BEGIN { print _-=log(_<_),-_,_-=_ _,-_ }‘

代码语言:javascript
复制
+inf -inf +nan -nan

因此,就指示函数与其数值之间的关系而言--(遗憾的是,NaNs还需要一个字符串比较,因为就awk而言,+nan == -nan是数字的):

函数is_inf(_) {传回is_negINF(_) \x\x}\x{返回-_<+_ & (+_)==-log(_<_) }函数is_negINF(_) {返回+_<&log;&log;&log;(+_)== log(_<_) }函数is_nan(_) {返回is_negNAN(_) \x- is_posNAN(_) }}函数is_posNAN (_ ) { return (_!~"-") && (_)== (_-=_=log(_<_)) }}函数is_negNAN(_) { return (_“-”) && (_)==(-(_-=_=log(_<_)}

代码语言:javascript
复制
 1  +inf -inf +nan -nan
 2  1 1 0 0 0 0
 3  1 0 1 0 0 0
 4  0 0 0 1 1 0
 5  0 0 0 1 0 1


 8  BEGIN { print __=_-=log(_), ___=-_, ____=_-=_, _____=-_

10      print is_inf(__), is_posINF(__), is_negINF(__),
              is_nan(__), is_posNAN(__), is_negNAN(__)

…       < repeat for the other 3 >       
18  }

这些方案绝不是包罗万象的;可能包含边缘案例的意外结果,

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51056836

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档