这是sqrt函数中的一个错误

时间:2012-05-30 11:55:21

标签: c++ assembly x86 standard-library x87

我创建了一个应用程序来计算64位范围内的素数,所以当我尝试使用sqrt中的math.h函数计算64位数的平方根时,我发现答案不是准确的,例如当输入为~0ull时,答案应为~0u但我得到的是0x100000000这是不对的,所以我决定使用汇编x86语言创建我自己的版本看看这是不是一个bug,这是我的功能:

inline unsigned prime_isqrt(unsigned long long value)
{
    const unsigned one = 1;
    const unsigned two = 2;

    __asm
    {
        test dword ptr [value+4], 0x80000000
        jz ZERO
        mov eax, dword ptr [value]
        mov ecx, dword ptr [value + 4]

        shrd eax, ecx, 1
        shr  ecx, 1
        mov  dword ptr [value],eax 
        mov  dword ptr [value+4],ecx

        fild value
        fimul two
        fiadd one
        jmp REST
ZERO: 
        fild value
REST: 
        fsqrt
        fisttp value
        mov eax, dword ptr [value]
    }
}

输入是奇数,以获得其平方根。当我使用相同的输入测试我的函数时,结果是相同的。

我没有得到的是为什么这些函数围绕结果或具体为什么sqrt指令结果?

2 个答案:

答案 0 :(得分:8)

sqrt不会舍入任何内容 - 当您将整数转换为double时,您会执行此操作。双精度不能代表64位整数可以不丢失精度的所有数字。特别是从2 53 开始,有多个整数将表示为相同的双精度值。

因此,如果将高于2 53 的整数转换为double,则会丢失一些最低有效位,这就是为什么(double)(~0ull)为18446744073709552000.0,而不是18446744073709551615.0(或更准确)后者实际上与前者相同,因为它们代表相同的双数。)

答案 1 :(得分:0)

你不太清楚你正在调用的C ++函数。 sqrt是一个重载的名称。你可能想要sqrt(double(~0ull))。没有sqrt重载需要unsigned long long