Ответ animal_magic верен: вы должны добавлять числа от наименьшего к наибольшему, однако я хочу привести пример, чтобы показать, почему.
Предположим, мы работаем в формате с плавающей запятой, который дает нам ошеломляющие 3 цифры точности. Теперь мы хотим добавить десять чисел:
[1000, 1, 1, 1, 1, 1, 1, 1, 1, 1]
Конечно, точный ответ - 1009, но мы не можем получить его в нашем 3-значном формате. Округляя до 3 цифр, мы получаем самый точный ответ - 1010. Если мы добавим наименьшее к наибольшему, то в каждом цикле получим:
Loop Index s
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 1009 -> 1010
Таким образом, мы получаем максимально точный ответ для нашего формата. Теперь давайте предположим, что мы добавляем от самого большого до самого маленького.
Loop Index s
1 1000
2 1001 -> 1000
3 1001 -> 1000
4 1001 -> 1000
5 1001 -> 1000
6 1001 -> 1000
7 1001 -> 1000
8 1001 -> 1000
9 1001 -> 1000
10 1001 -> 1000
Так как числа с плавающей запятой округляются после каждой операции, все сложения округляются, увеличивая нашу ошибку от 1 до 9 от точной. Теперь представьте, что ваш набор чисел для добавления имел 1000, а затем сто 1 или миллион. Обратите внимание, что, чтобы быть по-настоящему точным, вам нужно сложить наименьшие два числа, а затем преобразовать результат в набор чисел.
Предыдущие ответы уже обсуждают этот вопрос в целом и дают здравый совет, но есть еще одна особенность, о которой я хотел бы упомянуть. На большинстве современных архитектур
for
описанный вами цикл в любом случае будет выполняться с 80-битной расширенной точностью , что гарантирует дополнительную точность, поскольку все временные переменные будут занесены в регистры. Таким образом, у вас уже есть какая-то форма защиты от числовых ошибок. Однако в более сложных циклах промежуточные значения будут храниться в памяти между операциями и, следовательно, урезаться до 64 бит. я думаю чтодостаточно, чтобы получить более низкую точность суммирования (!!). Так что будьте очень осторожны, если вы хотите printf-отладить ваш код при проверке на точность.
Для интересующихся эта статья описывает проблему в широко используемой числовой программе (ранжирующая QR-факторизация Лапака), чья отладка и анализ были очень сложными именно из-за этой проблемы.
источник
Из двух вариантов, добавление от меньшего к большему приведет к меньшей числовой ошибке, чем добавление от большего к меньшему.
Однако> 20 лет назад в моем классе «Численные методы» преподаватель заявил об этом, и мне пришло в голову, что это все еще вносит больше ошибок, чем необходимо из-за относительной разницы в значении между аккумулятором и добавляемыми значениями.
Логически, предпочтительным решением является добавление 2 самых маленьких чисел в список, а затем повторное добавление суммированного значения в отсортированный список.
Чтобы продемонстрировать это, я разработал алгоритм, который мог бы сделать это эффективно (в пространстве и времени), используя пространство, высвобождаемое по мере удаления элементов из первичного массива, для создания вторичного массива суммированных значений, которые были изначально упорядочены с момента добавления были из сумм значений, которые всегда увеличивались. На каждой итерации проверяются «подсказки» обоих массивов, чтобы найти 2 наименьших значения.
источник
Поскольку вы не ограничивали используемый тип данных, для достижения совершенно точного результата просто используйте числа произвольной длины ... в этом случае порядок не будет иметь значения. Это будет намного медленнее, но достижение совершенства требует времени.
источник
Используйте сложение двоичного дерева, т. Е. Выберите среднее значение распределения (ближайшего числа) в качестве корня двоичного дерева и создайте отсортированное двоичное дерево, добавив меньшие значения слева от графика и большие значения справа и т. Д. , Добавить все дочерние узлы одного родителя рекурсивно в подходе снизу вверх. Это будет эффективно, так как ошибка avg возрастает с увеличением количества суммирований, а в подходе с двоичным деревом число суммирований имеет порядок log n в базе 2. Следовательно, ошибка avg будет меньше.
источник
То, что Христо Илиев сказал выше о 64-битных компиляторах, предпочитающих инструкции SSE и AVX по сравнению с FPU (AKA NDP), абсолютно верно, по крайней мере для Microsoft Visual Studio 2013. Однако для операций с плавающей запятой двойной точности, которые я использовал, я обнаружил, что это на самом деле быстрее, а теоретически более точно, использовать FPU. Если это важно для вас, я бы предложил сначала протестировать различные решения, прежде чем выбрать окончательный подход.
При работе в Java я очень часто использую тип данных BigDecimal произвольной точности. Это слишком просто, и обычно не замечают снижения скорости. Вычисление трансцендентных функций с бесконечными рядами и sqrt с использованием метода Ньютона может занять миллисекунду или более, но это выполнимо и довольно точно.
источник
Я только оставил это здесь /programming//a/58006104/860099 (когда вы идете туда, нажмите «показать фрагмент кода» и запустить его по кнопке
Это пример JavaScript, который ясно показывает, что сумма, начиная с наибольшей, дает большую ошибку
источник