2014-01-27 2 views
12

Почему выходной при добавлении одинаковых номеров?Различные результаты при добавлении одинаковых парных разрядов в другом порядке

public class Test { 

    public static void main(String a[]) { 

     double[] x = new double[]{3.9, 4.3, 3.6, 1.3, 2.6}; 
     System.out.println(">>>>>>> " + sum(x)); 
    } 

    public static double sum(double[] d) { 

     double sum = 0; 
     for (int i = 0; i < d.length; i++) { 
      sum += d[i]; 
     } 
     return sum; 
    } 
} 

Выход: 15.7

и если поменять местами значения

double[] x = new double[] {2.6, 3.9, 4.3, 3.6, 1.3}; 

Я получаю выход как: 15.700000000000001

Как получить тот же выход?

+11

Не обижайтесь, но почему так много upvotes? Еще один вопрос с плавающей точкой. Ответ один и тот же раз. – Radiodef

ответ

8

Числа с плавающей точкой lose precision, как вы делаете больше операций. Как правило, вы получаете максимальную точность, сначала добавляя наименьшие числа. (Таким образом, результат делает зависит от порядка операций)

В дополнение к поддержанию такой же порядок операций, вы также должны использовать strictfp, чтобы получить тот же результат на разных платформах.

Или еще лучше, не используйте плавающие точки: вместо этого используйте BigDecimal.

+0

Можете ли вы объяснить, почему он теряет точность, когда мы делаем больше операций? и почему мы получаем максимальную точность, сначала добавляя наименьшие числа? – Keerthivasan

+0

@Octopus Ошибка накапливается. См. [Распространение ошибки] (https://en.wikipedia.org/wiki/Propagation_of_uncertainty). (Думаю, я должен прекратить связывать википедию, но сейчас я немного ленив;) – Navin

+0

подумайте о примере 10/3. результат 3,333333 ... но компьютер должен остановиться где-то, поэтому он хранит информацию до определенной точности. Затем вы снова добавляете его в 10/3, а те отброшенные цифры в конце будут накапливаться до «ошибки» вашей операции, и это продолжается – Leo

0

потому что двойные и другие типы данных с плавающей точкой должны иметь дело с проблемами округления при выполнении операций. Точность не бесконечна. Если вы разделите 10/3, результат будет 3.33333333 ... но компьютер сохранит только часть этого числа.

проверка http://floating-point-gui.de/

+0

В этом ответе не рассматривается вопрос о том, почему изменение порядка суммирования влияет на результат. –

+0

мы можем показать биты и байты, но объяснение будет таким же ... – Leo

1

На каждом этапе последовательности арифметических операций с плавающей запятой система должна производить результат, который представляется в формате с плавающей запятой. Это может привести к ошибке округления, потере некоторой информации.

При добавлении двух чисел разных величин, более крупный имеет тенденцию контролировать, какие биты нужно отбрасывать. Если вы добавите большое и небольшое число, многие биты небольшого числа будут потеряны до ошибки округления из-за большой величины результата. Этот эффект уменьшается при добавлении числа одинаковой величины. Сначала добавив несколько небольших чисел, оставив большие числа величин до конца, позволяя накапливать эффект малых чисел.

Например, { 1e17, 21.0, 21.0, 21.0, 21.0, 21.0, 21.0, 21.0, -1e17 }. Точный ответ без какого-либо округления будет 147. Добавление в порядке, показанном выше, дает 112. Каждое добавление «21.0» должно быть округлено, чтобы соответствовать числу с величиной около 1e17. Добавление в порядке возрастания абсолютной величины дает 144, намного ближе к точному ответу. Частичный результат добавления 7 маленьких чисел равен 147, который затем должен быть округлен, чтобы соответствовать числу около 1e17.

0

Простое добавление всех значений приведет к сравнительно большой ошибке для более длинных массивов в любом случае (точнее: ошибка будет «большой», когда сумма уже «большая», а далее «маленькие» номера должны быть добавлено).

Как одна возможность уменьшить числовую ошибку, вы не могли бы рассмотреть http://en.wikipedia.org/wiki/Kahan_summation_algorithm:

public static double kahanSum(double d[]) 
{ 
    double sum = 0.0; 
    double c = 0.0; 
    for (int i=0; i<d.length; i++) 
    { 
     double y = d[i] - c; 
     double t = sum + y; 
     c = (t - sum) - y; 
     sum = t; 
    } 
    return sum;   
} 
Смежные вопросы