我想在Java中使用任意float或double并将其转换为有理数 - 即。一些形式a / b,其中a和b是长整数。我怎样才能以合理有效的方式做到这一点?
(顺便说一句 - 我已经有了用于简化分数的代码,所以a / b是否是最简单的形式并不重要。)
答案 0 :(得分:3)
首先看看double(或float,但我只提到下面的double)是constructed by IEEE-754 rules:
然后使用Double.doubleToLongBits
将double转换为位。
使用此方法1 + bit_0 * 2^(-1) + bit_1 * 2^(-2) ...
计算分数。
将结果乘以指数(2^(exponent)
为准确)并带有符号。
以下是代码:
double number = -0.15625;
// Code below doesn't work for 0 and NaN - just check before
long bits = Double.doubleToLongBits(number);
long sign = bits >>> 63;
long exponent = ((bits >>> 52) ^ (sign << 11)) - 1023;
long fraction = bits << 12; // bits are "reversed" but that's not a problem
long a = 1L;
long b = 1L;
for (int i = 63; i >= 12; i--) {
a = a * 2 + ((fraction >>> i) & 1);
b *= 2;
}
if (exponent > 0)
a *= 1 << exponent;
else
b *= 1 << -exponent;
if (sign == 1)
a *= -1;
// Here you have to simplify the fraction
System.out.println(a + "/" + b);
但要小心 - 对于大指数,你可能遇到不适合你变量的数字。实际上,您可以考虑沿分数存储指数,并且只有当指数足够小时才将其乘以。如果不是,你必须向用户显示分数,你可以使用科学记数法(这需要求解方程2^n = x * 10^m
,其中m是你的小数指数,x是你必须乘以分数的数字。但那是另一个问题......)。
答案 1 :(得分:2)
让long bits = Double.doubleToLongBits(double)
。来自Double.longBitsToDouble
的Javadoc:
...让s,e和m为可以从参数计算的三个值:
int s = ((bits >> 63) == 0) ? 1 : -1; int e = (int)((bits >> 52) & 0x7ffL); long m = (e == 0) ? (bits & 0xfffffffffffffL) << 1 : (bits & 0xfffffffffffffL) | 0x10000000000000L;
然后浮点结果等于数学表达式s·m·2 e-1075 的值。
这个结果肯定是一个有理数。
答案 2 :(得分:1)
对应于任何FP值的有理数是(尾数/ 2 ^ - 指数),其中尾数和指数如IEEE 754中所定义(Wiki参考)。然后,您可以通过LCD(或我猜GCF)应用分区来获得规范的有理数。
答案 3 :(得分:1)
对于给定的最大分母,Rubric continued fractions中包含的各种概念产生了最佳理性近似值。具体来说,您要问的是计算convergent sequence。在某些时候,当你的分母根据你想要的任何标准(或者通过有限整数实现长度强加给你)足够大时,终止计算收敛项并使用最后一项。在链接的维基百科页面上详细描述了算法。
为了解决您提出的一个问题,在收敛序列中生成的分数总是以简化形式。它们也可以证明是给定分母的最佳近似值。确切地说,形式m / n的收敛项比具有分母<1的另一个其他分数更接近目标数。 ñ。换句话说,收敛算法比试错法产生更好的近似值。
答案 4 :(得分:0)
如您所知,浮点数甚至不能存储simple numbers such as 0.1 exactly。如果你使用天真的方法来转换浮点数,那么你最终可能会得到巨大的分子和denomiators。
但是,有些算法可能有所帮助:Dragon4 and Grisu3算法旨在为浮点数创建最可读的输出。他们利用某些浮点位序列可以用几个小数表示,并选择最短的几位。
对于第一个实现,我将使用Dragon4和/或Grisu3来创建浮点之外的最短小数。例如,位cd cc cc cc cc cc f4 3f
的浮点数将导致小数 1.3 而不是 1.29999999 。然后,我将以a / b的形式表示小数部分并简化它。在给定的示例中,这将是 13/10 ,没有进一步的简化。
请注意,转换为小数部分可能是一个缺点。例如,有理数1/3不能用十进制和浮点数精确表示。所以,最好的解决方案是修改Dragon4这样的算法,使用任意的小数分母而不仅仅是10.唉,这几乎肯定需要大量的工作和一些CS背景。