有可能将浮点数双倍往返到两个十进制整数并保真吗?

时间:2013-05-14 12:57:44

标签: c ieee-754

我试图辨别是否可以将双精度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位中(即只存储文字位。)这里的目标是将整数部分和double的小数部分分解为整数表示(到接口) with和API,其存储格式我无法控制)并在重新组合两个64位整数时返回一个精确的双精度。

我对IEEE浮点有一个概念性的理解,我得到的是双精度存储的base-2。根据经验,我观察到,使用上述方法,即使是非常大的foo != bar值,有时也会<someConstant>。我已经离开学校一段时间了,我不能完全理解这个循环,理解这是否有可能给出不同的基础(或其他一些因素)。

编辑:

我想这是在我的大脑中隐含/理解但未在此处捕获:在这种情况下,我保证问题中双重的总体幅度将始终在+/- 2 ^ 63(和> 2) ^ -64)。根据这种理解,整数部分保证适合64位int类型,然后我的期望是小数精度为~16位,小数部分也应该很容易在64位int类型中表示。

3 个答案:

答案 0 :(得分: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;

如果你只想要几个可以重构值并且不关心那些整数含义的整数(例如,它们中的一个不必是整数部分),那么你可以使用{ {1}}将数字反汇编为有效数字(带符号)和指数,您可以使用frexp重新组合它:

ldexp

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

如果你想解决问题,甚至可以将int exp; int64_t I = frexp(foo, &exp) * 0x1p53; int64_t E = exp; double bar = ldexp(I, E-53); I打包到一个int64_t中。

答案 1 :(得分:1)

  

这里的目标是分解整数部分和小数部分   双重整数表示

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

双精度浮点数对于指数有11位,给出一系列值,例如2 -1022 ... 2 1023 。如果要存储整数和小数部分,则需要两个整数,每个整数大约有2个 10 位。然而,这是一种愚蠢的做事方式 - 大多数这些比特都会被闲置,因为只有尾数中的比特才有意义。使用两个非常长的整数可以让你在双重的整个范围内表示所有值,并且在任何地方都具有相同的精度,这是你无法用双精度表达的。例如,你可以有一个非常大的整数部分,其中一个非常小的部分,但这是一个双精度无法准确表示的数字。

<强>更新

如果您在评论中指出,您知道有问题的值在±2 63 范围内,您可以使用Extract fractional part of double *efficiently* in C的答案,如下所示:< / p>

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

我没有测试过,但是它应该能得到你想要的东西。

答案 2 :(得分:0)

请参阅维基百科,了解双重格式:

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;