【问题标题】:Why does awk produce different results for division by zero and how can I test for this?为什么 awk 除以零会产生不同的结果,我该如何测试?
【发布时间】:2013-01-29 11:35:16
【问题描述】:

在 mawk 中除以零时得到六个不同的结果:

$ echo | awk '{print -1/0 }' ; echo $?
-inf
0
$ echo | awk '{print 0/0 }' ; echo $?
-nan
0
$ echo | awk '{print 1/0 }' ; echo $?
inf
0
$ echo | awk '{printf ("%i\n", -1/0) }' ; echo $?
-2147483647
0
$ echo | awk '{printf ("%i\n", 0/0) }' ; echo $?
-2147483647
0
$ echo | awk '{printf ("%i\n", 1/0) }' ; echo $?
2147483647
0

我得到每个案例的“成功”退出代码。

  1. 为什么会这样?
  2. 我能做些什么呢?我是否总是必须在进行除法之前检查零除数,还是有什么方法可以依靠 awk 的错误处理和退出代码?

【问题讨论】:

    标签: error-handling awk divide-by-zero


    【解决方案1】:

    我猜你正在使用 mawkawknawkgawk 都没有这种行为,它们不支持除以零。

    您的答案是+inf 表示正无穷大,nan 表示“不是数字”,-inf 表示负无穷大,所有这些都符合预期。这些仅在打印 float 时输出,即使用%.6g,这是默认的OFMT(用于打印数字的默认格式)。

    $ mawk 'BEGIN {printf ("%i\n", 1/0) }'
    2147483647
    $ mawk 'BEGIN {printf ("%f\n", 1/0) }'
    inf
    $ gawk 'BEGIN {printf ("%f\n", 1/0) }'
    gawk: fatal: division by zero attempted
    

    当您使用“%i”显式打印时,您会得到 +HUGE 或 -HUGE,这将转换为 int(在您的情况下为带符号的 32 位)并打印为 + 或 - (2^31-1)。

    通常的做法是总是检查除以零,理想情况下,通过重新组织表达式来最小化您需要检查的次数——除以零会导致 awk 的其他实现简单地终止。

    mawk 正在构建时,它会检测您的 C 数学库的功能:

    $ ./configure
    [...]
    checking handling of floating point exceptions
        division by zero does not generate an exception
        overflow does not generate an exception
        math library supports ieee754
    

    如果您使用 NOINFO_SIGFPE 定义构建,您可以获得“标准”行为:

    $ ./configure CFLAGS="-DNOINFO_SIGFPE"
    $ make clean && make
    $ ./mawk 'BEGIN {printf ("%f\n", 1/0) }'
    mawk: run time error: division by zero
        FILENAME="" FNR=0 NR=0
    

    (虽然没有记录,但它可能不是您应该依赖的东西)。

    【讨论】:

      【解决方案2】:

      在尝试了不同的 AWK“方言”后,我得出了以下结论:

      尝试awk:

      echo | awk '{print -1/0 }' ; echo $?
      

      我得到:

      awk: (FILENAME=- FNR=1) fatal: division by zero attempted
      2
      

      尝试gawk:

      echo | gawk '{print -1/0 }' ; echo $?
      

      我得到:

      gawk: (FILENAME=- FNR=1) fatal: division by zero attempted
      2
      

      尝试mawk:

      echo | mawk '{print -1/0 }' ; echo $?
      

      我得到:

      -inf
      0
      

      如果我是你,我会尝试识别你的“默认方言”(awk --version),我会尝试使用另一种方言,例如 gawk,并阅读退出状态以了解发生了什么。

      PS:我运行的是 Ubuntu 11.10。

      PS:在我的例子中,awkgawk 的别名。

      【讨论】:

      • gawk 似乎是唯一具有--version 选项的版本。 mawk 使用 -W version,而我曾经使用的默认 awk(Ubuntu 默认)是 mawk。我开始认为gawk 可能会成为新安装的标准部分。
      • 由于限制,我在使用awk 时遇到了一些问题,这就是我使用gawk 的原因。
      【解决方案3】:

      甚至 gnu gawk 根据调用标志的不同表现出不同行为的组合并不难:

      (这些测试也更加简单,因为输出是基于调用 awk/start-up 选项的行为,而不是可以手动覆盖的参数常量。 p>

      我自己使用的 gawk 符号 (GMP/MPFR 不言自明)-

      E = -e aka standard call
      S = -S `—-sandbox`
      P = -P `--posix`
      c = -c `—-traditional`
      n = -n `--non-decimal-data`
      
          #  (-nan):x:gawk-S/c/E/mpfr
          #  (+inf):x:gawk-n/P
          #  (+nan):x:gawk-GMP/GMP+mpfr
          #   ( 0 ):x:nawk
          #   (inf):x:mawk
          #   (nan):x:mawk2
      
            x = -log((log(0)*log(0))^-log(-0))/(-"0xABCD")^-!-"";
      
          # (+nan) :gawk-GMP/MPFR
          #        (NaN:mpfr_log())
          #  (nan) :nawk/mawk/mawk2
          # (-nan) :gawk-E/P/c
      
              x = log(log((-log(0))/-log(0)));
      
          # (-inf):nawk
          #  (inf):mawk/mawk2
          # (+inf):gawk-ALL
      
              y = log(log((-log(0))^-log(0)));
      
          # (-inf):gawk-GMP
          # (-inf):nawk
          # (-nan):gawk-P/E/c
          #  (nan):mawk/mawk2
      
              z = log(-log(1))-(-"")^-!-"";
      
          # (+nan):gawk-GMP
          #  (inf):mawk/mawk2
          #  (nan):nawk
          # (+inf):gawk-except-GMP
      
              z = log(-log(0))-(-"")^-!-"";
      
          # (-inf):nawk/gawk-GMP
          #  (inf):mawk/mawk2
          # (+inf):gawk-except-GMP
      
              z = -(-"")^-!-"";
      
          #  (inf):nawk
          #  ( 0 ):gawk-GMP
          # (-inf):all others
      
              xn = (-"")^-!"";
      

      这些例子可能不只是被零除,但它确实强调了在边缘情况下,它们的行为变化很大。这是一个整理表,以便更容易理解:

          a = log(log((-log(0))^-log(0)));
          b = log(log((-log(0))/-log(0)));;
          c = -log((log(0)*log(0))^-log(-0))/(-"0xABCD")^-!-"";
          d = log(-log(0))-(-"")^-!-"";
          e = log(-log(1))-(-"")^-!-"";
      
      /usr/local/bin/nawk  ::  -inf ::  nan ::   0  ::  nan :: -inf ::
      ---------------------------------------------------------------
      /usr/local/bin/mawk  ::   inf ::  nan ::  inf ::  inf ::  nan ::
      ---------------------------------------------------------------
      /usr/local/bin/mawk2 ::   inf ::  nan ::  nan ::  inf ::  nan ::
      ---------------------------------------------------------------
      /usr/local/bin/gawk -e:: +inf :: -nan :: -nan :: +inf :: -nan ::
      /usr/...,/gawk -l mpfr:: +inf :: -nan :: -nan :: +inf :: -nan ::
      /usr/local/bin/gawk -S:: +inf :: -nan :: -nan :: +inf :: -nan ::
      /usr/local/bin/gawk -c:: +inf :: -nan :: -nan :: +inf :: -nan ::
      ---------------------------------------------------------------
      /usr/local/bin/gawk -P:: +inf :: -nan :: +inf :: +inf :: -nan ::
      /usr/local/bin/gawk -n:: +inf :: -nan :: +inf :: +inf :: -nan ::
      ----------------------------------------------------------------
      /usr/local/bin/gawk -M:: +inf :: +nan :: +nan :: +nan :: -inf ::
      ----------------------------------------------------------------
      

      【讨论】:

        猜你喜欢
        • 2017-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-12
        • 2021-11-10
        • 2017-02-21
        • 1970-01-01
        相关资源
        最近更新 更多