
VHDL 标准的浮点包 sqrt(2) 等计算结果出错
来源标题:The VHDL standard's float package gets sqrt(2) wrong
VHDL 标准(IEEE 1076-2008/2019)内置浮点包 ieee.float_pkg 被发现在 sqrt、mac、resize 等运算上给出错误结果,例如 float64 下 sqrt(2.0) 与精确舍入值相差 1 个最低位,mac(-6.0,-1.0,-5.0) 返回 -15.0 而非 1.0。该源码由 IEEE 自己维护,GHDL 和 NVC 原样分发;在 GHDL 7.0.0-dev 和 NVC 1.23.1 上相同输入产生相同错误输出。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
ieee.float_pkg 是 VHDL 标准(IEEE Std 1076-2008 与 1076-2019)的浮点包。源码来自 IEEE 自己:opensource.ieee.org/vasg/Packages 发布分支上的 ieee/float_generic_pkg-body.vhdl,共 5,712 行,$Revision: 1220 $。工作组在 gitlab.com/IEEE-P1076/Packages 的 develop 分支上是同样的字节,GHDL 和 NVC 也原样打包发布。这里的运行使用 GHDL 7.0.0-dev 和 NVC 1.23.1,两者运行相同测试向量时写出的结果文件完全一致。
本包与精确算术并排对照。一个操作数是一个符号和两个整数构成的小数,运算在小数上进行,结果按调用指定的舍入方式一次性舍入到目标格式。exact.py 就是那个参照。
使用本包自己的设置:round_nearest、3 个保护位、开启非规格化数。
调用 精确舍入结果 实际返回 sqrt (2.0) 在 float64 中 3FF6A09E667F3BCC 3FF6A09E667F3BCD sqrt (14.0) 406F7750 406F7751 sqrt (2.0**-149) 4.2e-22 3.7e-23 mac (-6.0, -1.0, -5.0) -15.0 1.0 mac (-5.0, 1.0, 4.0) 15.0 -1.0 mac (1.0, 1.0, -infinity) +infinity -infinity mac (1 + 2.0**-23, 1 - 2.0**-23, -1.0) -2.0**-26 -2.0**-46 resize (float64 中的 2.0**-512, 8, 23) 1.0 0.0 resize (float64 中的 2.0600, 8, 23) 2.088 +infinity resize (float64 中的 2.0**-1074, 8, 23) +infinity 0.0 resize (float64 中的 2.0**-650, 8, 23) 使模拟器停止 0.0 reciprocal (2.0127) +infinity 2.0-127 divide (2.0**-149, 2.0) 2.0**-149 0.0 dividebyp2 (3 * 2.0**-149, 2.0) 2.0**-149 2.0**-148 scalb (1.0, 300) 0.0 +infinity scalb (+infinity, -10) 2.0**118 +infinity to_signed (0.50000006, 32) 0 1 to_unsigned (199.78125, 8) 199 200
以及调用指定舍入方式的情况: divide (6.0, 3.0, round_inf) 2.0000002 2.0 reciprocal (2.0, round_inf) 0.50000006 0.5 add (largest, largest, round_zero) +infinity largest multiply (2.0**-100, 2.0**-100, round_inf) 0.0 2.0**-149 add (1.0, -1.0, round_neginf) +0.0 -0.0 to_signed (2.25, 8, round_inf) 2 3 to_float (100000, 5, 10, round_zero) +infinity 65504.0,即最大值
plain.vhd 发起这些调用,并在每个不是精确舍入结果的结果旁打印 WRONG:共 24 行,第 25 个调用在包体第 358 行因下标越界使模拟器停止。它只需要 GHDL: ghdl -a --std=08 plain.vhd ghdl --elab-run --std=08 plain
这些普通操作数并不罕见。在 1 到 4,096 的整数中,sqrt 对 256 个返回偏差一个单位的 float32,对 264 个返回偏差一个单位的 float64。在由 -6 到 6、0.25、0.5、1.5 和 10 构成的 4,913 个三元组中,mac 对 290 个返回错误的 float32。从 float64 到 float32 的 resize 对 2^-1074 到 2^-1024 的每个 2 的幂返回 +infinity,从 2^-662 到 2^-639 使模拟器停止,从 2^-638 到 2^-512 返回 2^(e+512),从 2^513 到 2^639 返回 2^(e-512)。
每种操作、格式和舍入方式各 10,000 次调用,操作数瞄准算术容易出问题的位置:非规格化操作数和结果、指数范围的两端、向上舍入进下一个 binade 的小数、精确的平局、带符号零、无穷大。单元格中的数字是不等于精确舍入结果的结果数,分母是比较的调用数。结果无法放入整数的转换不参与比较。
float32 round_nearest round_zero round_neginf round_inf add 0 / 10,000 304 / 10,000 312 / 10,000 159 / 10,000 subtract 0 / 10,000 314 / 10,000 312 / 10,000 143 / 10,000 multiply 0 / 10,000 1,102 / 10,000 781 / 10,000 775 / 10,000 divide 27 / 10,000 670 / 10,000 2,324 / 10,000 2,395 / 10,000 sqrt 735 / 10,000 581 / 10,000 542 / 10,000 774 / 10,000 mac 2,671 / 10,000 3,697 / 10,000 3,213 / 10,000 3,302 / 10,000 reciprocal 384 / 10,000 586 / 10,000 1,093 / 10,000 2,613 / 10,000 resize 到更窄格式 422 / 20,000 2,728 / 20,000 2,516 / 20,000 2,466 / 20,000 另有 272 次使模拟器停止 resize 到更宽格式 0 / 20,000 0 / 20,000 0 / 20,000 0 / 20,000 to_float(有符号,32 位和 64 位) 0 / 20,000 0 / 20,000 0 / 20,000 0 / 20,000 to_signed(8、32、64 位) 613 / 28,637 0 / 28,626 5,934 / 28,608 5,898 / 28,623 to_unsigned(8、32、64 位) 280 / 16,506 0 / 17,133 0 / 14,493 5,964 / 17,145 to_integer 0 / 10,000 0 / 10,000 1,921 / 10,000 1,973 / 10,000 to_float(整数) 0 / 10,000 0 / 10,000 0 / 10,000 0 / 10,000 dividebyp2 1,542 / 10,000 1,904 / 10,000 2,313 / 10,000 2,262 / 10,000 scalb 2,653 / 10,000 3,088 / 10,000 3,381 / 10,000 3,401 / 10,000
float64 round_nearest round_zero round_neginf round_inf add 0 / 10,000 260 / 10,000 311 / 10,000 138 / 10,000 subtract 0 / 10,000 283 / 10,000 317 / 10,000 147 / 10,000 multiply 0 / 10,000 1,163 / 10,000 852 / 10,000 866 / 10,000 divide 18 / 10,000 698 / 10,000 2,495 / 10,000 2,406 / 10,000 sqrt 1,278 / 10,000 619 / 10,000 659 / 10,000 799 / 10,000 mac 2,673 / 10,000 3,697 / 10,000 3,285 / 10,000 3,290 / 10,000 reciprocal 350 / 10,000 477 / 10,000 1,087 / 10,000 2,623 / 10,000 resize 到更窄格式 801 / 30,000 4,116 / 30,000 3,980 / 30,000 4,014 / 30,000 另有 445 次使模拟器停止 resize 到更宽格式 0 / 10,000 0 / 10,000 0 / 10,000 0 / 10,000 to_float(有符号,32 位和 64 位) 0 / 20,000 0 / 20,000 0 / 20,000 0 / 20,000 to_signed(8、32、64 位) 618 / 28,219 0 / 28,231 6,161 / 28,340 5,952 / 28,262 to_unsigned(8、32、64 位) 321 / 16,330 0 / 16,839 0 / 14,237 6,155 / 16,776 to_integer 0 / 10,000 0 / 10,000 2,125 / 10,000 2,183 / 10,000 to_float(整数) 0 / 10,000 0 / 10,000 0 / 10,000 0 / 10,000 dividebyp2 1,543 / 10,000 1,902 / 10,000 2,321 / 10,000 2,391 / 10,000 scalb 2,218 / 10,000 2,540 / 10,000 2,982 / 10,000 2,967 / 10,000
在 float32 中,859,771 次调用里有 81,068 次错误、272 次停止;在 float64 中,857,234 次调用里有 86,081 次错误、445 次停止。同样的调用在 float16 中,866,782 次里有 113,980 次错误;在 float128 中,855,777 次里有 90,744 次错误、585 次停止。
在格式足够小的地方,每个操作数都被覆盖: 格式 比较的调用数 错误数
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。