【问题标题】:Density of doubles between 2 given numbers两个给定数字之间的双精度数
【发布时间】:2017-12-31 20:36:02
【问题描述】:

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

新问题

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

对于双打,我应该使用一些我不知道的带有尾数和指数的公式。我已经有一个使用 nextafter 的代码,它在 [-1,1] 附近非常慢(低于 1e6 非常慢)

.

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

PS:如果您想知道,我正在为自己编写一些数学知识,我想知道在某些算法(例如高斯消元法,牛顿的求根方法等),为此我想采取一些措施。

【问题讨论】:

标签: c++ double


【解决方案1】:

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

您可以在恒定时间内计算一个范围内的双精度数,因为您可以通过从末尾减去开始并调整范围是打开还是关闭来计算恒定时间范围内的无符号整数。

有限非负范围内的双精度数具有形成连续整数序列的位模式。例如,范围 [1.0,2.0] 为范围 [0x3ff0_0000_0000_0000, 0x4000_0000_0000_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);
}

【讨论】:

  • 感谢您的回答。正如你所说,我不需要一个范围内的双精度和分数的确切数量,只需要一个很好的近似值来比较可以代表更多数字。范围也只能是非负的,因为 (2,3) 和 (-3,-2) 之间的密度相同。但是,我看不到如何使用此信息获得封闭形式的表达式。而且我猜即使它是线性的,计算每一个也有点慢,因为在最小的正整数之间有很多可表示的数字。
  • @Pernoctador 考虑获取一个范围内的整数个数。将其应用于unsigned long L = *(unsigned long*) &amp;d; 的结果,其中d 是你的双倍。
  • 太棒了!我已经测试过了,我没有发现任何问题。非常感谢。现在我需要得到一个合理的分数代码或数学表达式
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
相关资源
最近更新 更多