在接下来的内容中,包括该计划,我假设
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);
}