Java 从BigDecimal中获取约化分数

Java 从BigDecimal中获取约化分数,java,decimal,biginteger,bigdecimal,fractions,Java,Decimal,Biginteger,Bigdecimal,Fractions,我在做一些非常精确的十进制计算,最后我把它们转换成小分数。小数需要精确到96位小数 因为精度非常重要,所以我使用了BigDecimal和biginger BigDecimal的计算总是返回正确的十进制值,但在某些情况下,我将此十进制转换为小数的函数失败 假设我有一个大的十进制d d.toString() = 32.22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222

我在做一些非常精确的十进制计算,最后我把它们转换成小分数。小数需要精确到96位小数

因为精度非常重要,所以我使用了BigDecimal和biginger

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;
    }
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


//现在我们减少 返回reduceFractionnumerator.intValue、denominator.intValue; 在这种情况下,这一定是失败的,因为这里的分子或分母都不适合int

分子变为-1908874353,分母在调用intValue后变为0。在计算结束之前,必须携带大整数

在将它们转换为int或long之前,如果必须这样做,可以通过检查Integer.MIN\u值、Integer.MAX\u值、long.MIN\u值和long.MAX\u值来检查它们是否可以在不损失精度的情况下转换为这些类型。

//现在我们减少 返回reduceFractionnumerator.intValue、denominator.intValue; 在这种情况下,这一定是失败的,因为这里的分子或分母都不适合int

分子变为-1908874353,分母在调用intValue后变为0。在计算结束之前,必须携带大整数


在将它们转换为int或long之前,如果必须这样做,您可以通过检查Integer.MIN\u值、Integer.MAX\u值、long.MIN\u值和long.MAX\u值来检查它们是否可以在不损失精度的情况下转换为这些类型。

您似乎使这比需要的困难得多。以下是我的初步尝试:

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是负数,那么有理数返回时分子为负数。

您似乎使这比需要的困难得多。以下是我的初步尝试:

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为负数,则返回有理数时,分子为负数。

我做了一些更改,将每个BigDecimal的整数值而不是整数值发送到reduceFractionBigDecimal分子,BigDecimal分母似乎计算不正确,你知道为什么吗?你对GCD的理解是错误的。您提供的值不会除以分子。你的分子是290*g+33,其中g是你建议的GCD。Java实现是正确的。我做了一些更改,将BigDecimal而不是每个BigDecimal的intValue发送到reduceFractionBigDecimal分子,BigDecimal分母似乎计算的gcd值不正确,你知道为什么吗?你对gcd的实际含义是错误的。您提供的值不会除以分子。你的分子是290*g+33,其中g是你建议的GCD。Java实现是正确的。
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)};
    }
}