【问题标题】:Get reduced fraction from BigDecimal从 BigDecimal 获得减少的分数
【发布时间】:2020-05-22 13:32:05
【问题描述】:

我正在做一些非常精确的十进制计算,最后我会变成简化的分数。小数点需要精确到小数点后 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

【问题讨论】:

    标签: java decimal biginteger bigdecimal fractions


    【解决方案1】:
    // Now we reduce
    return reduceFraction(numerator.intValue(), denominator.intValue());
    

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

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

    在将它们转换为intlong 之前,如果必须这样做,您可以检查它们是否可以在不损失精度的情况下转换为这些类型,方法是检查它们与Integer.MIN_VALUEInteger.MAX_VALUE、@987654328 @ 和 Long.MAX_VALUE

    【讨论】:

    • 我进行了更改,将 BigDecimals 而不是每个 BigDecimal 的 intValue 发送到 reduceFraction(BigDecimal numerator, BigDecimal denominator) 似乎 gcd 值计算不正确,您知道原因吗?
    • 您对 GCD 的实际含义是错误的。您提供的值不会除分子。您的分子是 290 * g + 33,其中 g 是您提出的 GCD。 Java 实现是正确的。
    【解决方案2】:

    您似乎使这件事变得比需要的困难得多。这是我最初的尝试:

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

    【讨论】:

      猜你喜欢
      • 2018-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-09
      • 1970-01-01
      • 2021-06-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多