首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Juniper浮点方案/bug中粘性位丢失的细节是什么?

Juniper浮点方案/bug中粘性位丢失的细节是什么?
EN

Stack Overflow用户
提问于 2021-07-31 12:19:24
回答 1查看 67关注 0票数 0

在某些Juniper MX路由器上,浮点数的处理不正确:如果在计算过程中向右移动超过8位(下溢),粘性位就会丢失。有解决这个问题的办法吗?有什么已知的影响吗?修好了吗?这是IEEE可接受的选项吗?其他系统中是否存在此问题?

Math Details示例(最好使用固定宽度字体和宽屏查看):

代码语言:javascript
复制
                                                                                                                                                                          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)
                   ^                                                         ^                        ^                                                        ^
EN

Stack Overflow用户

发布于 2021-08-04 06:04:54

Internet source告诉我,许多MX系列路由器使用英特尔x86 CPU。当x87配置为在扩展精度模式下运行时,观察到的行为与使用AVX进行浮点计算完全一致(与SSE或x87相反)。

x87将所有操作数存储在80位寄存器中,其中每个寄存器使用64个有效数(尾数)位保存浮点操作数,并且有效数的整数位是显式的。FPU控制字的位8和9表示精度控制字段,其指示FPU将在哪个位位置对结果进行舍入。设置为2相当于双精度,而设置为3表示舍入到扩展精度。

大多数类Unix的32位操作系统将x87取整控制设置为3,而Windows将其设置为2。我不知道现在的Junos是32位还是64位操作系统。出于向后兼容的原因,它可以保留使用x87和FPU精度控制设置3。

当x87精度控制设置为3(扩展精度)时,存在双舍入问题。浮点运算的结果首先舍入到扩展精度,并存储在内部FPU寄存器中。稍后,数据从寄存器中取出,并在将此结果存储到与double变量对应的内存位置时再次四舍五入。

为了方便访问x87汇编语言指令,我使用英特尔编译器根据Windows64上的问题编写了具体的方案。该程序以三种不同的格式(十进制浮点、十六进制浮点和二进制)转储两个源操作数ab以及总和r,并转储这些操作数的内部80位表示形式(带有t前缀)。

通过将USE_X87_EXTENDED_PRECISION定义为01,可以在计算之前将USE_X87_EXTENDED_PRECISION的精度控制设置为双精度或扩展精度,并且相关FPU控制字的值显示为compute cw。将USE_X87_EXTENDED_PRECISION设置为0时,程序的输出为:

代码语言:javascript
复制
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_PRECISION1时,结果为:

代码语言:javascript
复制
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,因为超过舍入位的所有尾随有效位都是0,所以默认舍入模式的“偶数”部分“舍入到最近的或偶数”开始起作用。

代码语言:javascript
复制
#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, a, double_as_uint64 (a), ta.h, ta.l);
    printf ("b=%23.16e %21.13a %016llx tb=%04x%016llx\n", b, b, double_as_uint64 (b), tb.h, tb.l);
    printf ("r=%23.16e %21.13a %016llx tr=%04x%016llx\n", r, r, double_as_uint64 (r), tr.h, tr.l);

    return EXIT_SUCCESS;
}
票数 3
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68602043

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档