将double转换为BigRational(分子/分母为两个BigInteger)



我有一个用java定制的BigRational类。它被实现为两个BigInteger,表示分子和分母。

我有一个";从字符串";方法,该方法以"-1234/43";但我想实现from double/from float;我并不害怕生成一个很大的数字,但我想保持浮点表示中的所有精度;因此,如果我把它们转换成十进制表示,我会因为四舍五入而失去精度。

-如何生成一对BigInteger,将其解释为分子/分母表示与给定浮点/双精度相同的精确数字?

(希望在Java中,我不需要担心bigendian/litletendian,但我也想要一个确认(

所以,多亏了一位好朋友,我找到了一个很好的解决方案,所以我会在这里为任何需要的人发布它。它没有使用任何字符串表示,所以它也应该很快。我已经测试过了";合理地";它结合工作并保持精确的表示。当然,我们仍然应该添加一些"if"来处理NAN。

final static int mantissaBits=53;
public static BigRational from(double num){
int exponent=Math.getExponent(num);
long man=Math.round(Math.scalb(num, mantissaBits-exponent));
long den=Math.round(Math.scalb(1.0, mantissaBits-exponent));
return new BigRational(BigInteger.valueOf(man),BigInteger.valueOf(den));
}

注意:并非所有数字都是有理的,例如PI不是有理数。然而,考虑到double(和float(的精度有限,浮点值中的位数有限,因此您总能找到一个有理数。例如,Math.PI是具有值3.141592653589793double。这个数就是有理数3_141_592_653_589_793 / 1_000_000_000_000_000

理解浮点值不准确的警告,可以在BigDecimal的帮助下找到有理数,然后使用BigInteger.gcd()规范化有理数。

像这样:

static void printAsRational(double value) {
printAsRational(BigDecimal.valueOf(value));
}
static void printAsRational(float value) {
printAsRational(new BigDecimal(Float.toString(value)));
}
static void printAsRational(BigDecimal value) {
BigInteger numerator, denominator;
if (value.signum() == 0) {
// Zero is 0 / 1
numerator = BigInteger.ZERO;
denominator = BigInteger.ONE;
} else {
BigDecimal bd = value.stripTrailingZeros(); // E.g. 1.20 -> 1.2
if (bd.scale() < 0)
bd = bd.setScale(0); // E.g. 1.7e3 -> 1700
numerator = bd.unscaledValue(); // E.g. 1.25 -> 125
denominator = BigDecimal.valueOf(1, -bd.scale()).toBigInteger(); // E.g. 1.25 -> 100

// Normalize, e.g. 12/8 -> 3/2
BigInteger gcd = numerator.gcd(denominator);
if (! gcd.equals(BigInteger.ONE)) {
numerator = numerator.divide(gcd);
denominator = denominator.divide(gcd);
}
}
System.out.println(value + " = " + numerator + " / " + denominator);
}

测试

printAsRational(Math.PI);
printAsRational(Math.E);
printAsRational(1.25);
printAsRational(1);
printAsRational(0);
printAsRational(-1.25);
printAsRational(1.25e9);
printAsRational(1.25e-9);

输出

3.141592653589793 = 3141592653589793 / 1000000000000000
2.718281828459045 = 543656365691809 / 200000000000000
1.25 = 5 / 4
1.0 = 1 / 1
0.0 = 0 / 1
-1.25 = -5 / 4
1.25E+9 = 1250000000 / 1
1.25E-9 = 1 / 800000000

最新更新