数字比较在awk中产生不正确的结果
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数字比较在awk中产生不正确的结果相关的知识,希望对你有一定的参考价值。
我最近在网站上找到了一个脚本:
bash, find nearest next value, forward and backward
这是相对陈旧的,需要50个代表评论,我没有。我试图让它工作,并且不太了解awk语法,但我正在尝试。在我正在使用的测试文件中:
-3.793 0.9804E+00 0.3000E+02
-3.560 0.1924E-01 0.3000E+02
-3.327 0.3051E-04 0.3000E+02
-3.093 0.3567E-08 0.3000E+02
-2.860 0.3765E-06 0.3000E+02
-2.627 0.1119E-02 0.3000E+02
-2.394 0.2520E+00 0.3006E+02
这是脚本:
{
if ($fld > tgt) {
del = $fld - tgt
if ( (del < minGtDel) || (++gtHit == 1) ) {
minGtDel = del
minGtVal = $fld
}
}
else if ($fld < tgt) {
del = tgt - $fld
if ( (del < minLtDel) || (++ltHit == 1) ) {
minLtDel = del
minLtVal = $fld
}
}
else {
minEqVal = $fld
}
}
END {
print (minGtVal == "" ? "NaN" : minGtVal)
print (minLtVal == "" ? "NaN" : minLtVal)
}
当这样运行时:
$ awk -v fld=1 -v tgt=-3 -f awk DOSCAR
生产:
-2.860
NaN
即使有一个下限,我也不太确定如何解决它。原帖没有负数,所以他们没有这个问题。任何帮助表示赞赏。
输入文件中有一个空行,触发了经典的awk陷阱。
核心问题是awk比较运算符的奇怪行为,它不需要您指定是否需要数字或字符串比较。 (<意见>这正是为什么自动比较算子是一个坏主意。</ opinion>)
简而言之,awk中有三种标量类型:数字,字符串和“数字字符串”。程序中的文字是数字或字符串,算术运算符的结果总是一个数字,而字符串连接的结果总是一个字符串。但是你所比较的值 - $fld
和tgt
--都是潜在的“数字字符串”,因为它们来自用户输入。
“数字字符串”是来自用户输入的字符串,恰好“看起来像”一个数字。总的来说,“看起来像一个数字”的定义并不令人惊讶,除了一个细节:空字符串不计算。
如果比较两个数字,则比较是数字。如果比较两个字符串,则比较是字典。但是,如果您要比较的一个(或两个)可能是“数字字符串”,那么比较的类型取决于它是否实际上是“数字字符串”。如果它是“数字字符串”,它会变成一个数字;否则,另一个值变成一个字符串。
因此,如果$fld
是一个空字符串,那么将它与tgt
进行比较将是字符串比较而不是数字比较。空字符串是字符串比较的最小可能字符串,因此它会变小。但是,当你计算$fld - tgt
时,$fld
将被强制转换为数字,在这种情况下,空字符串变为0。
所以有两种可能性。最简单的方法是强制将$fld
改为数字;这至少是一致的:
{
val = $fld + 0
if (val > tgt) {
del = val - tgt
if ( (del < minGtDel) || (++gtHit == 1) ) {
minGtDel = del
minGtVal = val
}
}
else if (val < tgt) {
del = tgt - val
if ( (del < minLtDel) || (++ltHit == 1) ) {
minLtDel = del
minLtVal = val
}
}
else {
minEqVal = val
}
}
END {
print (minGtVal == "" ? "NaN" : minGtVal)
print (minLtVal == "" ? "NaN" : minLtVal)
}
另一种方法是消除指示字段不能是数字的行。对数值进行简单且通常可靠的测试是将值与自身进行比较,并将其作为一个数字进行比较:
(val = $fld + 0) == $fld {
if (val > tgt) {
del = val - tgt
if ( (del < minGtDel) || (++gtHit == 1) ) {
minGtDel = del
minGtVal = val
}
}
else if (val < tgt) {
del = tgt - val
if ( (del < minLtDel) || (++ltHit == 1) ) {
minLtDel = del
minLtVal = val
}
}
else {
minEqVal = val
}
}
END {
print (minGtVal == "" ? "NaN" : minGtVal)
print (minLtVal == "" ? "NaN" : minLtVal)
}
以上是关于数字比较在awk中产生不正确的结果的主要内容,如果未能解决你的问题,请参考以下文章
TelemetryClient 在 Application Insights 中产生不一致的结果
GNU awk (gawk) 中涉及 NaN 的令人惊讶的数值比较结果