关于Java:从BigDecimal获取减少的分数

Get reduced fraction from BigDecimal

我正在做一些非常精确的十进制计算,最后我将其转换为减少的分数。小数位数需要精确到96个小数位数。

由于精度非常重要,因此我使用BigDecimal和BigInteger。

BigDecimal的计算总是返回正确的十进制值,但是在某些情况下,我无法将此十进制转换为小数的函数

假设我有一个BigDecimal d

1
d.toString() = 32.222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223

当我的函数试图将其转换为分数时,它输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

这是我的将小数减为小数的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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 []

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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返回:

1
2
// Now we reduce, send BigDecimals for numerator and denominator
        return reduceFraction(num, den);

而不是

1
2
// Now we reduce
        return reduceFraction(numerator.intValue(), denominator.intValue());

仍然从函数得到错误答案

输出分数现在为

1
2
3
4
5
// Gcd value
gcd = 1

// Fraction is then:
32222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223/1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
1
2
3
4
5
//gcd Value should be:
gcd = 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

// Whit this gcd the fraction reduces to:
290/9

1
2
// Now we reduce
return reduceFraction(numerator.intValue(), denominator.intValue());

好吧,在这种情况下这一定会失败,因为这里的分子或分母都不适合int

分子变为-1908874353,对它们调用intValue()后分母变为0。您必须携带BigIntegers直到计算结束。

在将它们转换为intlong之前,如果必须这样做,可以通过将它们与Integer.MIN_VALUEInteger.MAX_VALUELong.MAX_VALUE


您似乎正在使这项工作变得更加困难。这是我最初的尝试:

1
2
3
4
5
6
7
8
9
10
11
12
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为负数,则返回分子为负数的有理数。