在Java中将浮点数转换为Rational数?

时间:2012-11-04 20:38:50

标签: java floating-point rational-numbers

  

可能重复:
  How can I turn a floating point number into the closest fraction represented by a byte numerator and denominator?

我想在Java中使用任意float或double并将其转换为有理数 - 即。一些形式a / b,其中a和b是长整数。我怎样才能以合理有效的方式做到这一点?

(顺便说一句 - 我已经有了用于简化分数的代码,所以a / b是否是最简单的形式并不重要。)

5 个答案:

答案 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背景。