在awk中产生错误结果的数值比较

在awk中产生错误结果的数值比较,awk,numeric,Awk,Numeric,我最近在网站上找到了一个脚本: 这是相对老,需要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.1

我最近在网站上找到了一个脚本:

这是相对老,需要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)
}
产生:

 -2.860
 NaN

即使有一个下限,我也不太确定如何修正它。原来的帖子里没有负数,所以他们没有这个问题。感谢您的帮助

您的输入文件中有一个空行,这将触发一个经典的awk gotcha

核心问题是awk比较运算符的奇怪行为,它不需要您指定是要进行数字比较还是字符串比较。(这正是为什么自动比较运算符是个坏主意。)

简而言之,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
如果(val>tgt){
del=val-tgt
如果((del
另一种方法是消除指示字段不能为数字的行。一个简单且通常可靠的数值测试是将数值与强制为数字的数值进行比较:

(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)
}
(val=$fld+0)==$fld{
如果(val>tgt){
del=val-tgt
如果((del
这是一个纯awk脚本——里面根本没有bash,问题不应该有
bash
标记。(想想如果有人问“我如何在bash中执行X”,然后得到一个使用python的答案——关于这个答案的后续问题是python问题,而不是bash问题)。顺便说一句,这个问题写得不是很好——你还应该说明你的预期输出是什么,并描述实际输出错误的原因。回答很好,谢谢我的大测试文件的末尾确实有一个空格,很好!我不知道awk就是这样工作的+1.
(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)
}