0

我试图辨别是否可以将双精度 IEEE 浮点值分解为两个整数,并在以后完全保真地重新组合它们。想象一下这样的事情:

double foo = <inputValue>;
double ipart = 0;
double fpart = modf(foo, &ipart);

int64_t intIPart = ipart;
int64_t intFPart = fpart * <someConstant>;

double bar = ((double)ipart) + ((double)intFPart) / <someConstant>;

assert(foo == bar);

逻辑上很明显,任何 64 位数量都可以存储在 128 位中(即只存储文字位。)这里的目标是将双精度的整数部分和小数部分分解为整数表示形式(与 API 接口)我无法控制其存储格式)并在重新组合两个 64 位整数时返回一个位精确的双精度数。

我对 IEEE 浮点有一个概念性的理解,并且我知道双精度数是以 2 为基数存储的。我凭经验观察到,使用上述方法,有时foo != bar甚至对于非常大的<someConstant>. 我已经离开学校一段时间了,我无法完全理解在不同的基础(或其他一些因素)下这是否可能。

编辑:

我想这是在我的大脑中暗示/理解的,但在这里没有捕捉到:在这种情况下,我保证问题中双精度的总体幅度将始终在 +/- 2^63 以内(并且 > 2^-64) . 有了这种理解,整数部分可以保证适合 64 位 int 类型,那么我的期望是,在大约 16 位的十进制精度下,小数部分也应该很容易用 64 位 int 类型表示。

4

3 回答 3

5

如果您知道数字在 [–2 63 , +2 63 ) 中并且 ULP(数字中最低位的值)至少为 2 -63,那么您可以使用以下命令:

double ipart;
double fpart = modf(foo, &ipart);

int64_t intIPart = ipart;
int64_t intFPart = fpart * 0x1p63;

double bar = intIPart + intFPart * 0x1p-63;

如果您只想要几个可以从中重构值的整数并且不关心这些整数的含义(例如,其中一个不必是整数部分),那么您可以使用frexp反汇编数字到它的有效数(带符号)和指数,你可以用ldexp它来重新组装它:

int exp;
int64_t I = frexp(foo, &exp) * 0x1p53;
int64_t E = exp;

double bar = ldexp(I, E-53);

此代码适用于 IEEE-754 64 位二进制浮点对象的任何有限值。它不支持无穷大或 NaN。

如果您想解决问题,甚至可以将其打包成单个Iint64_t 。E

于 2013-05-14T13:40:28.763 回答
1

这里的目标是将双精度的整数部分和小数部分分解为整数表示

你甚至不能可靠地得到整数部分或小数部分。问题是您似乎误解了浮点数的存储方式。它们没有整数部分和小数部分。它们有一个有效数字部分,称为尾数和一个指数。指数本质上是向上或向下缩放尾数,类似于科学记数法的工作原理。

双精度浮点数的指数有 11 位,给出的值范围类似于 2 -1022 ...2 1023。如果要存储整数和小数部分,则需要两个整数,每个整数大约有 2个 10位。不过,那将是一种愚蠢的做事方式——这些位中的大多数都不会被使用,因为只有尾数中的位是重要的。使用两个非常长的整数可以让您以相同的精度表示整个 double 范围内的所有值,这是您无法使用 double 完成的。例如,您可以有一个非常大的整数部分和一个非常小的小数部分,但这是一个双精度数无法准确表示的数字。

更新

如果,正如您在评论中指出的那样,您知道所讨论的值在 ±2 63范围内,您可以使用在 C 中提取 double *efficiently* 的小数部分的答案,如下所示:

double whole = // your original value
long iPart = (long)whole;
double fraction = whole - iPart;
long fPart = fraction * (2 << 63);

我还没有测试过,但它应该可以得到你想要的。

于 2013-05-14T13:24:13.340 回答
0

请参阅 wikipedia 以了解双精度数的格式:

http://en.wikipedia.org/wiki/Double-precision_floating-point_format

IEEE 双精度格式编码三个整数:有效数、指数和符号位。以下代码将以 IEEE 双精度格式提取三个组成整数:

double d = 2.0;  

// sign bit
bool s = (*reinterpret_cast<int64_t*>(&d)) >> 63;

// significand
int64_t m = *reinterpret_cast<int64_t*>(&d) & 0x000FFFFFFFFFFFFFULL;

// exponent
int64_t e = ((*reinterpret_cast<int64_t*>(&d) >> 52) & 0x00000000000007FFULL) - 1023;

// now the double d is exactly equal to s * (1 + (m / 2^52)) * 2^e
// print out the exact arithmatic expression for d:

std::cout << "d = " << std::dec << (s ? "-(1 + " : "(1 + (") << m << "/" << (1ULL << 52) << ")) x 2^" << e;
于 2013-05-14T13:36:13.820 回答