主核上无法使用指数为 0 的浮点数(subnormals),报错 sig 8(SIGFPE)



  • 1 总结

    1.1 报错

    warning: node [26528]: user's mpe task: tid= 0, pid= 22346, terminated by sig 8
    

    1.2 解决

    使用 sw5cc 编译时,主核上的浮点数默认似乎不遵守 IEEE 标准,指数为 0 的浮点数(subnormals)会被置为 0。如果在主核代码中尝试使用一个 subnormal,会报错 SIGFPE。

    打开编译选项 -OPT:IEEE_arith,主核上能够正常使用 subnormals 了。在后面记录中出现的问题,可以通过以下编译选项解决:

    • -OPT:IEEE_arith=1,或
    • -OPT:IEEE_arith=2

    2 记录

    2.1 代码

    使用手册里的计算除法的 C 语言例子,在主、从核上使用相同的表达式不断把浮点数除以 1000(数组索引为 j, ij 也是线程编号,一共除以 (j+1)*i 次 1000)。随后数据由从核传回主核,把所有数据加起来作为 checksum 比较。

    2.2 编译选项

    sw5cc 和 sw5gcc 都试了。主核和从核使用相同选项, -O1-O2-O3 都一样报错。

    2.3 结果

    浮点数用 float,从核只用 2 个时,发生以下情形:

    • 从核上出现的小于 2^(−127)≈5.877e−39 的数字可以在主核端打印,但不能用于四则运算,否则报错(SIGFPE);
    • 但是这些数据打印出来的值都超出了 float 的范围。

    具体输出结果如下。其中,数组 c 在从核上计算,数组 cc 在主核上计算,紧跟数组值的是 c 中元素的十六进制。

    从核 0 的结果:

    c[0][0] = 3.6666666623209573e-18, cc[0][0] = 3.6666666623209573e-18
    22 87 46 b0
    c[0][1] = 3.6666666849391771e-21, cc[0][1] = 3.6666666849391771e-21
    1d 8a 85 d3
    c[0][2] = 3.6666665082343344e-24, cc[0][2] = 3.6666665082343344e-24
    18 8d d8 e8
    c[0][3] = 3.6666663171820839e-27, cc[0][3] = 3.6666663171820839e-27
    13 91 40 6a
    c[0][4] = 3.6666663021357562e-30, cc[0][4] = 3.6666663021357562e-30
    0e 94 bc d7
    c[0][5] = 3.6666662639321898e-33, cc[0][5] = 3.6666662639321898e-33
    09 98 4e af
    c[0][6] = 3.6666663500279674e-36, cc[0][6] = 3.6666663500279674e-36
    04 9b f6 76
    c[0][7] = 6.9405734356891773e-309, cc[0][7] = 0.0000000000000000e+00
    00 27 ed 2d
    c[0][8] = 6.9415787311951471e-312, cc[0][8] = 0.0000000000000000e+00
    00 00 0a 39
    c[0][9] = 7.9574842161197712e-315, cc[0][9] = 0.0000000000000000e+00
    00 00 00 03
    c[0][10] = 0.0000000000000000e+00, cc[0][10] = 0.0000000000000000e+00
    00 00 00 00
    

    从核 1 的结果:

    c[1][0] = 3.6666666623209573e-18, cc[1][0] = 3.6666666623209573e-18
    22 87 46 b0
    c[1][1] = 3.6666665082343344e-24, cc[1][1] = 3.6666665082343344e-24
    18 8d d8 e8
    c[1][2] = 3.6666663021357562e-30, cc[1][2] = 3.6666663021357562e-30
    0e 94 bc d7
    c[1][3] = 3.6666663500279674e-36, cc[1][3] = 3.6666663500279674e-36
    04 9b f6 76
    c[1][4] = 6.9415787311951471e-312, cc[1][4] = 0.0000000000000000e+00
    00 00 0a 39
    c[1][5] = 0.0000000000000000e+00, cc[1][5] = 0.0000000000000000e+00
    00 00 00 00
    

    两个从核在循环次数不同的情况下,能够打印出数量不同的 subnormals,并且打印出的结果显示这些数字远远小于 float 类型允许的 subnormals。
    其中一个数字使用 printf("%.16e\n") 打印出来为
    - 十进制:7.9574842161197712e-315
    - 十六进制:00 00 00 03
    - 二进制:0000 0000 0000 0000 0000 0000 0000 0011

    00 00 00 03 这个十六进制数实际对应的 4 字节单精度浮点数为 4.2039e-45
    7.9574842161197712e-315 这个浮点数按 8 字节双精度处理,转换成十六进制是 00 00 00 00 60 00 00 00

    2.4 补充

    主核上似乎无法使用 subnormals。

    float 类型的最后一个指数非 0 的数字是 0x00800000,这个用于计算不会报错。第一个指数为 0 的数字是 0x007fffff,这个数字用于计算会报错。

    2.5 问题

    请问这是什么情况?主核和从核对浮点数 subnormal 的处理不一样?
    有没有办法避免这种情况?在从核上加一个判断把这些数字全部置为 0 是可以正常执行的,但这样有点浪费时间。



  • 已解决,编译时加上:

    • -OPT:IEEE_arith=1,或
    • -OPT:IEEE_arith=2


  • 感谢!解决了问题!🙏



  • 感谢楼主提示,这里对新的编译器以及fortran上类似问题做出一些补充

    补充:最近申威提供了新版编译器如530和710,这些C或Fortran的编译器无法使用-OPT:IEEE_arith=1选项,可以使用-mieee -mfloat-ieee -mieee-conformant这几个选项代替。详细说明可以使用swgfortran -v --help查看

    摘录部分输出内容如下

      -mfloat-ieee                Do not use VAX fp
      -mfloat-vax                 Use VAX fp
      -mfp-regs                   Use fp registers
      -mfp-rounding-mode=         Control the generated fp rounding mode
      -mfp-trap-mode=             Control the IEEE trap mode
      -mgas                       Assume GAS
      -mieee                      Emit IEEE-conformant code, without inexact
                                  exceptions
      -mieee-conformant           Request IEEE-conformant math library routines
                                  (OSF/1)
      -mieee-with-inexact         This switch lacks documentation
    

登录后回复