代码之家  ›  专栏  ›  技术社区  ›  Pernoctador

两个给定数字之间的双精度密度

  •  1
  • Pernoctador  · 技术社区  · 8 年前

    重要编辑:最初的问题是关于获得双倍和分数的密度。当我得到双倍而不是分数的答案时,我改变话题来结束这个问题。原始问题的另一半是 here

    新问题

    我想找出两个给定数字之间的双倍密度,但我想不出一个好方法。所以我在寻找一个闭式表达式doublesIn(a,b)。或者一些在合理时间内完成工作的代码。

    对于双倍,我应该用一些公式来表示尾数和指数,我不知道。我已经有了一个使用 下一个之后 它的速度非常慢,接近[-1,1](1e6以下的速度非常慢)

    .

    有什么想法吗?提前感谢!:)

    PS:如果你想知道的话,我正在为自己编写一些数学资料,我想知道在某些算法(如高斯消元法、牛顿求根法等)上用分数(长、长或类似)代替double有多有用,为此我想做些测量。

    1 回复  |  直到 8 年前
        1
  •  1
  •   Patricia Shanahan    8 年前

    在接下来的内容中,包括该计划,我假设 double 由IEEE 754 64位二进制浮点表示。这是最可能的情况,但C++标准不能保证。

    可以在恒定时间内计算某个范围内的双倍数,因为可以在恒定时间内计算某个范围内的无符号整数,方法是从结束处减去开始处,然后调整该范围是打开的还是关闭的。

    有限非负范围内的双精度数具有形成连续整数序列的位模式。例如,范围[1.0,2.0]对于范围[0x3ff0\u 0000\u 0000\u 0000,0x4000\u 0000\u 0000]中的每个整数都包含一个双精度。

    有限的非正范围内的双精度数的行为方式相同,只是无符号位模式的值随着双精度数变得更负而增加。

    如果您的范围包括正数和负数,请将其拆分为零,以便处理一个非负数范围和另一个非正数范围。

    大多数并发症都是在你想要准确计算的时候发生的。在这种情况下,您需要调整范围是打开还是关闭,并精确地计算一次零。

    就你而言,在几亿人中减少一到两人可能没什么大不了的。

    这里有一个简单的程序来演示这个想法。它几乎没有收到错误检查,因此使用风险自负。

    #include <iostream>
    #include <cmath>
    using namespace std;
    
    uint64_t count(double start, double end);
    
    void testit(uint64_t expected, double start, double end) {
        cout << hex << "Should be " << expected << ": " << count(start, end)
                << endl;
    }
    
    double increment(double data, int count) {
        int i;
        for (i = 0; i < count; i++) {
            data = nextafter(data, INFINITY);
        }
        return data;
    }
    
    double decrement(double data, int count) {
        int i;
        for (i = 0; i < count; i++) {
            data = nextafter(data, -INFINITY);
        }
        return data;
    }
    
    int main() {
        testit((uint64_t) 1 << 52, 1.0, 2.0);
        testit(5, 3.0, increment(3.0, 5));
        testit(2, decrement(0, 1), increment(0, 1));
        testit((uint64_t) 1 << 52, -2.0, -1.0);
        testit(1, -0.0, increment(0, 1));
        testit(10, decrement(0,10), -0.0);
        return 0;
    }
    
    // Return the bit pattern representing a double as
    // a 64-bit unsigned integer.
    uint64_t toInteger(double data) {
        return *reinterpret_cast<uint64_t *>(&data);
    }
    
    // Count the doubles in a range, assuming double
    // is IEEE 754 64-bit binary.
    // Counts [start,end), including start but excluding end
    uint64_t count(double start, double end) {
        if (!(isfinite(start) && isfinite(end) && start <= end)) {
            // Insert real error handling here
            cerr << "error" << endl;
            return 0;
        }
        if (start < 0) {
            if (end < 0) {
                return count(fabs(end), fabs(start));
            } else if (end == 0) {
                return count(0, fabs(start));
            } else {
                return count(start, 0) + count(0, end);
            }
        }
        if (start == -0.0) {
            start = 0.0;
        }
        return toInteger(end) - toInteger(start);
    }