2015-05-08 2 views
1

Мне очень понравился алгоритм округления, потому что в CS нам нужно было эмулировать HP35 без использования библиотеки Math. Мы не включили алгоритм округления в нашу окончательную сборку, но я все равно хотел это сделать.Java: алгоритм двойного округления

public class Round { 
    public static void main(String[] args) { 

     /* 
     * Rounds by using modulus subtraction 
     */ 
     double a = 1.123599; 

     // Should you port this to another method, you can take this as a parameter 
     int b = 5; 

     double accuracy = Math.pow(10, -b); 

     double remainder = a % accuracy; 

     if (remainder >= 5 * accuracy/10) // Divide by ten is important because remainder is smaller than accuracy 
      a += accuracy; 

     a -= remainder; 


     /* 
     * Removes round off error done by modulus 
     */ 
     String string = Double.toString(a); 

     int index = string.indexOf('.') + b; 

     string = string.substring(0, index); 

     a = Double.parseDouble(string); 

     System.out.println(a); 


    } 
} 

Это хороший алгоритм или есть лучшие? Я не забочусь о тех, которые определены в Java API, я просто хотел знать, как это было сделано.

[EDIT] Вот код, который я придумал после того, как глядя на ответ EJP в

public class Round { 
    public static void main(String[] args) { 

     double a = -1.1234599; 
     int b = 5; 
     boolean negative = a < 0; 

     if (negative) a = -a; 

     String string = Double.toString(a); 
     char array[] = string.toCharArray(); 

     int index = string.indexOf('.') + b; 
     int i = index; 

     int value; 
     if (Character.getNumericValue(array[index +1]) >= 5) { 

      for (; i > 0; i--) { 
       value = Character.getNumericValue(array[i]); 

       if (value != -1) { 
        ++value; 
        String temp = Integer.toString(value) 
        array[i] = temp.charAt(temp.length()-1); 
        if (value <= 9) break; 
       } 
      } 
     } 

     string = ""; 
     for (int j=0; j < index + 1 ; j++) { 
      string += array[j]; 
     } 

     a = Double.parseDouble(string); 

     if (negative) a =-a; 

     System.out.println(a); 
    } 
} 

ответ

4

Номера с плавающей запятой не имеют десятичных знаков. У них есть двоичные места, и они не соизмеримы. Любая попытка изменить переменную с плавающей запятой, чтобы иметь определенное количество десятичных знаков, равна , обреченной на провал.

Вы должны выполнить округление до заданного количества знаков после запятой после преобразования в десятичный радиус.

+0

Мой алгоритм работает хотя (хотя и не для отрицательных). Каким образом они «обречены на провал»? – Lightfire228

+1

Ваш алгоритм работает для нескольких значений, с которыми вы его протестировали. Попробуйте все возможные значения. Он обречен на неудачу в этом, * большую часть времени *, он не будет давать значения с точно заданным количеством знаков после запятой. Это невозможно. – EJP

+0

Итак, если я конвертирую double в массив, в котором каждый элемент содержит каждое десятичное число, затем округляет массив и переносит каждую цифру переполнения (т.е. .999 + .001 = .99 + .01 = .9 + 1) отдельно , было бы похоже на преобразование его в _decimal radix_? – Lightfire228

0

Есть еще несколько способов круглых чисел. Документация RoundingMode для Java (введенная в 1.5) должна дать вам краткое введение в различные методы, которые люди используют.

Я знаю, что вы сказали, что не имеют доступа к Math функций, но самое простое округление вы можете сделать, это:

public static double round(double d) 
{ 
    return Math.floor(d + 0.5); 
} 

Если вы не хотите использовать любыеMath функции, вы мог бы попробовать что-то вроде этого:

public static double round(double d) 
{ 
    return (long)(d + 0.5); 
} 

Эти два, возможно, в некоторых ситуациях ведут себя по-разному (отрицательные числа?).

+0

Проблема с этим заключается в том, что они не позволяют пользователю определять точность, которой они хотят иметь плавающую точку. Программа, которую я предоставил, может округлять до цифр 'b' после десятичной точки. – Lightfire228

+0

Вы, конечно, правы. Популярным методом округления с произвольной точностью является использование логики, например, вашего первого примера: умножение, округление, разделение назад. Это может вызвать проблемы, если вы имеете дело с числами, которые могут переполняться при умножении на точность 10 ^. Однако мне не удавалось найти альтернативы. Единственное, что я мог бы предложить, это нечто вроде [метода Ньютона] (http://en.wikipedia.org/wiki/Newton%27s_method) - добавлять/вычитать последовательно меньшие значения до тех пор, пока они не будут в пределах некоторого допуска определенной точности. Однако я не знаю, как проверить переносимость. –

+0

@AlexTaylor «Популярный метод» не работает большую часть времени. См. [Здесь] (http://stackoverflow.com/a/7593617/207421) для доказательства. – EJP

Смежные вопросы