Преобразование десятичной дроби в дробь

У меня есть метод, который преобразует десятичное число (двойное значение) в дробь и помещает значения числителя и знаменателя в int[] размера 2.

Тестирование работает нормально для большинства значений, за исключением случаев, когда я нажимаю 0.0001. Тогда возвращаемое значение равно 1.0/1.0.

Метод:

private static int[] toFractionPos(double x){
    String[] parts = Double.toString(x).split("\\.");
    double den = Math.pow(10, parts[1].length()); //denominator
    double num = Double.parseDouble(parts[0]) * den + Double.parseDouble(parts[1]); //numerator
    return reduceFraction((int)num, (int)den);
}

reduceFraction() метод:

public static int[] reduceFraction(int num, int den){
    int gcf = GCF(num, den); //greatest common factor
    int[] rf = {num/gcf, den/gcf};
    return rf;
}

Спасибо!


person Dando18    schedule 14.03.2015    source источник
comment
Может быть, ошибки точности?   -  person EDToaster    schedule 14.03.2015
comment
Двойное значение не является десятичным. Это представление с плавающей запятой с двоичными цифрами. Ничего десятичного в этом нет. Вы имеете в виду число с дробной частью?   -  person user207421    schedule 03.07.2017


Ответы (4)


Алгоритм вроде нормальный. Однако использование double не подходит для такого рода задач, поскольку точность уменьшается по мере увеличения масштаба.

Вы должны использовать BigDecimal и BigInteger вместо этого. Я грубо изменил ваш пример, чтобы он работал с ними, но я не позаботился о деталях, т. Е. Разбор String не должен быть необходим, поскольку масштаб можно получить из BigDecimal с геттером, вы можете настроить другое округление режимы и т.д.:

import java.math.BigDecimal;
import java.math.BigInteger;

public class Sample {

    static int[] toFractionPos(BigDecimal x) {
        String[] parts = x.toString().split("\\.");
        BigDecimal den = BigDecimal.TEN.pow(parts[1].length()); // denominator
        BigDecimal num = (new BigDecimal(parts[0]).multiply(den)).add(new BigDecimal(parts[1])); // numerator
        return reduceFraction(num.intValue(), den.intValue());
    }

    static int[] reduceFraction(int num, int den) {
        int gcd = BigInteger.valueOf(num).gcd(BigInteger.valueOf(den)).intValue(); // greatest
                                                                                   // common
                                                                                   // divisor
        int[] rf = { num / gcd, den / gcd };
        return rf;
    }

    public static void main(String[] args) {
        int[] fraction = toFractionPos(new BigDecimal("0.0001"));
        System.out.println(fraction[0] + "/" + fraction[1]); // 1/10000
    }
}

Примечание: оптимизация оставлена ​​в качестве упражнения ;)

person fps    schedule 14.03.2015

Вы не должны работать с двойниками, так как вы теряете точность, и это может привести к серьезным ошибкам. Но в случае с 1.0001 проблема в том, что:

Double.toString(1.0001) == "1.0E-4"

Затем вы пытаетесь проанализировать "0E-4" и получаете 0 вместо 1. Вы можете сделать следующее, если ожидаете не более 10 десятичных знаков:

DecimalFormat df = new DecimalFormat("0", 
    DecimalFormatSymbols.getInstance(Locale.ENGLISH));
df.setMaximumFractionDigits(10);
String[] parts = df.format(x).split("\\.");
person JuniorCompressor    schedule 14.03.2015

Как насчет этого?

private static int[] toFractionPos(double x){
  int den = (int)Math.pow(10,(int)Math.log10(Integer.MAX_VALUE));
  int num = (int)(x*den);
  return reduceFraction(num, den);//this came from your code
}
person AlShibli    schedule 15.02.2016

Я думаю, это сработает,

public int[] Fraction(double n) {
   BigDecimal p = BigDecimal.ONE;
   BigDecimal dn = BigDecimal.valueOf(n);       
   while(true){
        dn = dn.multiply(p);
        if( dn.compareTo(new BigDecimal(dn.toBigInteger()))==0 )
            break;
        else
            p = p.multiply(BigDecimal.TEN);
   }
   BigInteger num=dn.toBigInteger(), den=p.toBigInteger(), g=num.gcd(den);
   num = num.divide(g);
   den = den.divide(g);             
   int[] res = new int[2];
   res[0] = num.intValue();
   res[0] = den.intValue();
   return res;
}
person user3870075    schedule 03.07.2017
comment
Это не дает полезный ответ на вопрос. почему вы считаете, что это ответ? как это работает? Простое указание кому-то изменить свой код без какого-либо контекста или смысла не поможет ему понять, что он сделал неправильно. - person GrumpyCrouton; 03.07.2017
comment
Я отредактировал свой комментарий, чтобы ответить на вопрос полезным способом. - person user3870075; 04.07.2017