如何解决丢失粘滞位的瞻博网络浮点方案/错误的详细信息是什么?
在某些瞻博网络 MX 路由器上,浮点数未正确处理:如果在计算过程中向右移动超过 8 位(下溢),粘滞位就会丢失。有解决方法吗?是否有任何已知的影响?是否已修复?这是 IEEE 可接受的选项吗?其他系统是否存在该问题?
数学详细信息示例(最好使用固定宽度字体和宽屏幕查看):
1
shifts: 12345678901
4095.05615204245304994401521980762481689453125000000000 = 0x1.ffe1cbff5e3e1p+11 = 0x40affe1cbff5e3e1 = 111111111111.00001110010111111111101011110001111100001
+ 1.0000137123424794882708965815254487097263336181640625 = 0x1.0000e60e10001p+0 = 0x3ff0000170168000 = 1.0000000000000000111001100000111000010000000000000001
^
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1000000000000.000011100110000011100001000000000000000010s
LGRS
0101
1 2 3 4 5
mantissa bit #: 1234567890123 4567890123456789012345678901234567890123
4096.0561657547959839575923979282379150390625 = 0x1.0000e60e10001p+12 = 0x40b0000e60e10001 = 1000000000000.0000111001100000111000010000000000000001 (on "all" systems/correct)
4096.056165754795074462890625 = 0x1.0000e60e10000p+12 = 0x40b0000e60e10000 = 1000000000000.0000111001100000111000010000000000000000 (on Juniper router)
^ ^ ^ ^
解决方法
Internet source 告诉我许多 MX 系列路由器使用 Intel x86 CPU。当 x87 配置为在扩展精度模式下运行时,观察到的行为与使用 x87 FPU 进行浮点计算(而不是 SSE 或 AVX)完全一致。
x87 FPU 将所有操作数存储在 80 位寄存器中,其中每个寄存器使用 64 个有效数(尾数)位保存一个浮点操作数,有效数的整数位是显式的。 FPU 控制字的第 8 位和第 9 位表示精度控制字段,指示 FPU 将舍入结果的位位置。设置为 2 相当于双精度,设置为 3 表示舍入到扩展精度。
大多数类 Unix 32 位操作系统将 x87 舍入控制设置为 3,而 Windows 将其设置为 2。我不知道现代 Junos 是 32 位还是 64 位操作系统。出于向后兼容的原因,它可能会保留使用 x87 和 FPU 精度控制设置 3。
当 x87 精度控制设置为 3(扩展精度)时,双舍入会出现问题。浮点运算的结果首先四舍五入到扩展精度并存储在内部 FPU 寄存器中。之后,数据从寄存器中取出并再次四舍五入,当这个结果存储到与 double
变量对应的内存位置时。
我使用英特尔编译器从 Windows64 上的问题中编写了特定场景,以便轻松访问 x87 汇编语言指令。程序以三种不同的格式(十进制浮点数、十六进制浮点数和二进制)转储两个源操作数 a
和 b
以及和 r
并转储内部 80 位这些操作数的表示(带有 t
前缀)。
通过将 USE_X87_EXTENDED_PRECISION
定义为 0
或 1
,FPU 的精度控制可以在计算之前设置为双精度或扩展精度,以及相关的值FPU 控制字显示为compute cw
。将 USE_X87_EXTENDED_PRECISION
设置为 0
,程序的输出为:
original cw=027f
compute cw=027f
a=4.0950561520424530e+003 0x1.ffe1cbff5e3e1p+11 40affe1cbff5e3e1 ta=400afff0e5ffaf1f0800
b=1.0000137123424795e+000 0x1.0000e60e10001p+0 3ff0000e60e10001 tb=3fff8000730708000800
r=4.0960561657547960e+003 0x1.0000e60e10001p+12 40b0000e60e10001 tr=400b8000730708000800
然而,当 USE_X87_EXTENDED_PRECISION
为 1
时,结果为:
original cw=027f
compute cw=037f
a=4.0950561520424530e+003 0x1.ffe1cbff5e3e1p+11 40affe1cbff5e3e1 ta=400afff0e5ffaf1f0800
b=1.0000137123424795e+000 0x1.0000e60e10001p+0 3ff0000e60e10001 tb=3fff8000730708000800
r=4.0960561657547951e+003 0x1.0000e60e10000p+12 40b0000e60e10000 tr=400b8000730708000400
在第二次舍入期间,从 tr
到 r
,舍入位为 1,但粘性位为 0,因为舍入位之后的所有尾随有效数位为零,因此“偶数”部分默认舍入模式“舍入到最近或偶数”开始。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define USE_X87_EXTENDED_PRECISION (1)
typedef struct tbyte {
uint64_t l;
uint16_t h;
} tbyte;
uint64_t double_as_uint64 (double a)
{
uint64_t r; memcpy (&r,&a,sizeof r); return r;
}
int main (void)
{
double a = 0x1.ffe1cbff5e3e1p+11;
double b = 0x1.0000e60e10001p+0;
double r;
uint16_t cw_orig,cw_comp,cw_temp;
tbyte ta,tb,tr;
__asm fstcw word ptr [cw_orig];
#if USE_X87_EXTENDED_PRECISION
cw_temp = cw_orig | (3 << 8);
__asm fldcw word ptr [cw_temp];
#endif // USE_X87_EXTENDED_PRECISION
__asm fstcw word ptr [cw_comp];
__asm fld qword ptr [a];
__asm fld qword ptr [b];
__asm fld st(1);
__asm fadd st,st(1);
__asm fst qword ptr [r];
__asm fstp tbyte ptr [tr];
__asm fstp tbyte ptr [tb];
__asm fstp tbyte ptr [ta];
__asm fldcw word ptr [cw_orig];
printf ("original cw=%04x\n",cw_orig);
printf ("compute cw=%04x\n",cw_comp);
printf ("a=%23.16e %21.13a %016llx ta=%04x%016llx\n",a,double_as_uint64 (a),ta.h,ta.l);
printf ("b=%23.16e %21.13a %016llx tb=%04x%016llx\n",b,double_as_uint64 (b),tb.h,tb.l);
printf ("r=%23.16e %21.13a %016llx tr=%04x%016llx\n",r,double_as_uint64 (r),tr.h,tr.l);
return EXIT_SUCCESS;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。