我正在做一些非常精确的小数计算,最后将其转换为约分数。小数点需要精确到 96 位小数。
由于精度非常重要,我使用 BigDecimal 和 BigInteger。
BigDecimal 的计算始终返回正确的十进制值,但我将此小数转换为分数的函数在某些情况下会失败
假设我有一个 BigDecimal d
d.toString() = 32.222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223
当我的函数尝试将其转换为它输出的分数时
Decimal from BigDecimal is:
32.222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223
// Run the BigDecimal into getFraction
Denominator before reducing:
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Numerator before reducing:
32222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223
// Reduced fraction turns into:
-1/0
// But should output
290/9
这是我将小数化为分数的函数:
static int[] getFraction(BigDecimal x) {
BigDecimal x1 = x.stripTrailingZeros();
//System.out.println(x.toString() + " stripped from zeroes");
//System.out.println(x1.scale());
// If scale is 0 or under we got a whole number fraction something/1
if(x1.scale() <= 0) {
//System.out.println("Whole number");
int[] rf = { x.intValue(), 1 };
return rf;
}
// If the decimal is
if(x.compareTo(BigDecimal.ZERO) < 0) {
// Add "-" to fraction when printing from main function
// Flip boolean to indicate negative decimal number
negative = true;
// Flip the BigDecimal
x = x.negate();
// Perform same function on flipped
return getFraction(x);
}
// Split BigDecimal into the intval and fractional val as strings
String[] parts = x.toString().split("\\.");
// Get starting numerator and denominator
BigDecimal denominator = BigDecimal.TEN.pow(parts[1].length());
System.out.println("Denominator :" + denominator.toString());
BigDecimal numerator = (new BigDecimal(parts[0]).multiply(denominator)).add(new BigDecimal(parts[1]));
System.out.println("Numerator :" + numerator.toString());
// Now we reduce
return reduceFraction(numerator.intValue(), denominator.intValue());
}
static int[] reduceFraction(int numerator, int denominator) {
// First find gcd
int gcd = BigInteger.valueOf(numerator).gcd(BigInteger.valueOf(denominator)).intValue();
//System.out.println(gcd);
// Then divide numerator and denominator by gcd
int[] reduced = { numerator / gcd, denominator / gcd };
// Return the fraction
return reduced;
}
如果有人能澄清我是否犯了任何错误,我将不胜感激!
** 更新 **
更改了reduceFraction函数: 现在返回 String[] 而不是 int[]
static String[] reduceFraction(BigDecimal numerator, BigDecimal denominator) {
// First find gcd
BigInteger nu = new BigInteger(numerator.toString());
BigInteger de = new BigInteger(denominator.toString());
BigInteger gcd = nu.gcd(de);
// Then divide numerator and denominator by gcd
nu = nu.divide(gcd);
de = de.divide(gcd);
String[] reduced = { nu.toString(), de.toString() };
// Return the fraction
return reduced;
}
getFraction 返回:
// Now we reduce, send BigDecimals for numerator and denominator
return reduceFraction(num, den);
而不是
// Now we reduce
return reduceFraction(numerator.intValue(), denominator.intValue());
仍然从函数中得到错误的答案
现在的输出分数是
// Gcd value
gcd = 1
// Fraction is then:
32222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223/1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
//gcd Value should be:
gcd = 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
// Whit this gcd the fraction reduces to:
290/9
最佳答案
你似乎让事情变得比需要的困难得多。这是我的初步尝试:
public static BigInteger[] toRational(BigDecimal decimal)
{
int scale = decimal.scale();
if (scale <= 0) {
return new BigInteger[]{decimal.toBigInteger(), BigInteger.ONE};
} else {
BigInteger denominator = BigInteger.TEN.pow(scale);
BigInteger numerator = decimal.unscaledValue();
BigInteger d = numerator.gcd(denominator);
return new BigInteger[]{numerator.divide(d), denominator.divide(d)};
}
}
有理数总是以最低项返回。请注意,如果decimal
为 0,则返回 0/1 作为有理数。如果decimal
为负数,则返回有理数,分子为负数。
关于java - 从 BigDecimal 获取约简分数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61956255/