0

如何获取long long(64 位)值abdouble(64 位)值d(double)a / b或多或少等于d?这可能吗(不损失精度)?

我已经按照这个思路进行了尝试,但没有成功,所以我想也许我有错误的想法:

union ieee754_double u;
u.d = d;

long long a = (long long)u.ieee.mantissa0 << 32 | u.ieee.mantissa1;
long long b = (long long)1 << (u.ieee.exponent + IEEE754_DOUBLE_BIAS);
4

4 回答 4

5

除了无穷大和 NaN 之外,每个浮点数都可以精确地表示为两个整数的比率。一些双精度浮点数确实需要大于 64 位的整数——例如,1e-300将转换为6032057205060441 / (2 ** 1049). 但是,近似范围内的浮点数(2**-40, 2**63)可以无损地转换为两个 64 位整数的分数。

这种转换函数的一个例子是 Python 的as_integer_ratio()浮点对象方法。从 Python/C-ese 翻译,代码如下所示:

#include <math.h>
#include <stdlib.h>

void double_as_ratio(double flt, long long *numerator, long long *denominator)
{
    double float_part;
    int exponent;
    long long long_exponent;
    int i;

    float_part = frexp(flt, &exponent);  /* flt == float_part * 2**exponent exactly */
    for (i=0; i<300 && float_part != floor(float_part) ; i++) {
        float_part *= 2.0;
        exponent--;
    }
    /* flt == float_part * 2**exponent exactly and float_part is integral. */

    *numerator = (long long) float_part;           /* can overflow */
    long_exponent = 1LL << labs((long) exponent);  /* can overflow */
    if (exponent > 0) {
        *numerator *= long_exponent;
        *denominator = 1;
    }
    else
        *denominator = long_exponent;
}

此代码不依赖于位的确切布局,而仅依赖于C89 所需的frexp和函数。floor应用于浮点值,它会产生和0.1的正确值。360287970189639736028797018963968

于 2013-04-30T10:54:12.673 回答
2

你到底想做什么?如果你正在转变double为理性人,你几乎肯定想要一个近似的答案。

你希望它有多准确?如果答案正好是 244653797/159601597,你希望这就是答案吗?我非常怀疑。你想更喜欢小数吗?还是小分母的分数?或者是什么?

0.4286 应该等于 4286/10000 = 2143/500 还是 1/7?

0.428 应该是 107/250 还是 1/7?

在不知道您实际上要解决什么问题的情况下,很难解决它。

于 2013-04-30T11:01:20.597 回答
1

Dan Steffy为理性重构编写了代码,该代码非常简单易读。如果你想要a这样四舍五入ba/b正确double并且b相当小,请bits在调用时将参数设置reconstruct_bits为 53。

我相信代码通过连续分数近似工作。需要注意的是,对于给定的分母界或相对误差界,这并不一定会产生最佳的有理逼近。它产生了所有可能的分母界限最小化相关数量(目前我不知道)的所有理性重建。

于 2013-04-30T11:28:50.827 回答
0

根据评论,其他一些答案和纯粹的跟踪和错误,我想出了这个(这似乎或多或少的工作):

union ieee754_double u;
u.d = d;

long long a = (long long)(!u.ieee.exponent && u.ieee.exponent != 0x7ff) << 52 |
        (long long)u.ieee.mantissa0 << 32 | u.ieee.mantissa1;
int exp = IEEE754_DOUBLE_BIAS - u.ieee.exponent + 52;

long long b;
if (u.ieee.exponent != 0x7ff) {
    if (exp > 62) {
        a >>= exp - 62;
        exp = 62;
    }
    if (exp < 0) {
        a <<= 0 - exp;
        exp = 0;
    }
    b = 1LL << exp;
} else {
    b = 0;
}

a = u.ieee.negative ? -a : a;

我仍在尝试找出边缘在哪里,但是由于我正在移出位,因此我认为+/-2**62上下无损2**-10

atof(argv[1])使用-->进行一些往返测试printf(),我似乎能够精确地重现值+/-2**532**-18只要我四舍五入)。我猜这2**53是一个限制double

于 2013-05-02T06:18:28.553 回答