如何从long long
(64位)值a
获取b
(64位)值double
和d
,以便{ {1}}或多或少等于(double)a / b
?这是否可能(不会损失精确度)?
我已经尝试过这个但是它没有得到任何地方,所以我想也许我的想法是错误的:
d
答案 0 :(得分: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
,它会生成3602879701896397
和36028797018963968
的正确值。
答案 1 :(得分:2)
你到底想要做什么?如果您要将double
转换为理性,那么您几乎肯定需要一个近似的答案。
您希望它有多准确?如果答案恰好是244653797/159601597,您希望这是答案吗?我非常怀疑。你想要小数部分吗?还是具有小数分母的分数?或者是什么?
0.4286应该是4286/10000 = 2143/500还是1/7?
0.428应该是107/250还是1/7?
在不知道您实际想要解决的问题的情况下,很难解决它。
答案 2 :(得分:1)
丹·斯蒂芬写道code for rational reconstruction这是非常简单和有益的阅读。如果您希望a
和b
以便a/b
轮到正确的double
和b
相当小,请在调用时设置bits
参数reconstruct_bits
到53。
我相信代码通过连续的分数近似来工作。值得注意的是,对于给定的分母界限或相对误差界限,这并不一定产生 最佳有理逼近。它产生了所有合理的重建,最大限度地减少了所有可能的分母边界的相关数量(目前无法实现)。
答案 3 :(得分: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
并降至{{1} }
使用2**-10
- &gt;进行一些往返测试atof(argv[1])
),我似乎能够将值精确地重现到printf()
和+/-2**53
(只要我是圆的)。我猜测2**-18
是2**53
的限制。