使用double的整数sqrt的准确性

时间:2017-12-04 17:39:10

标签: floating-point square-root

我想计算uint64_t的整数部分。对于32位uint32_t,通常建议先将其转换为doublesqrt,然后再将其强制转换为uint32_t

它是否适用于uint64_t,因为double可以完全容纳最多2 ^ 53的数字?即,以下总是会给出正确答案:

#include <math.h>
uint64_t x = ...;
uint64_t result = (uint64_t)sqrt((double)x);

甚至:

#include <math.h>
uint64_t x = ...;
uint32_t result = (uint32_t)sqrt((double)x);

1 个答案:

答案 0 :(得分:4)

根据经验,答案是。输入4503599761588224的结果错误地计算为67108865而不是67108864。

以下代码标识了这种情况。 1 当然,您可以删除break;以观察其他情况。

#include <stdio.h>
#include <stdint.h>
#include <math.h>

int main(void) {
    for (uint32_t y = 1; y != 0; y++) {
        // *Just* smaller than a perfect square
        uint64_t x = ((uint64_t)y * (uint64_t)y) - 1;

        // We expect the floor of the result     
        uint32_t expected = y - 1;

        uint32_t result = (uint32_t)sqrt((double)x);

        if (result != expected) {
            printf("Incorrect: x = %llu, result = %u\n", x, result);
            break;
        }
    }
    return 0;
}

关于价值4503599761588224有什么特别之处?那么,它确切地(2 26 + 1) 2 - 1,AKA(2 52 + 2 27 < / SUP>)。这可以用double精确表示,因此错误不是由于long - &gt; double转化。

相反,错误是sqrt实现的内部错误。三角洲(相对于一个完美的正方形)在这里将平方根减少大约2 -27 ,这比result本身小约2 53 倍。这是双精度可以处理的极限,因此我们自然希望在此时看到一个一个错误。 2

<子> 1。 Live demo

<子> 2。感谢@EricPostpischil在以下评论中确定根本原因:)