avva: (Default)
[personal profile] avva
Числа с плавающей точкой - вот еще одно в череде компьютерных задач, которыми я занимался мало и понимаю плохо. Сегодня я узнал, что числа с плавающей точкой надо складывать - если их много - не "наивно", а особенным образом. Мне неприятно, что я до сих пор этого не знал; или если знал, то забыл - кажется логичным, чтобы это упоминалось во время курса по численному анализу, который я брал на втором курсе много лет назад, но это был один из наиболее нелюбимых предметов, и после экзамена я с радостью забыл вообще все, что его касается.

Эта тема всплыла в одной внутренней рассылке на работе, где утверждалось, что средний выпускник CS-факультета этого не знает. Я уже давно не выпускник, но честно признаюсь: не знал. А вы, коллеги, знали?

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

В Перле:

DB<1> $a=0.1234567890123456
DB<2> $x = 1_000_000*$a;
DB<3> for (1..1_000_000) { $y += $a; }
DB<4> x $y;
0 123456.789009695
DB<5> x $x
0 123456.789012346
DB<6> x $y-$x;
0 '-2.65109702013433e-06'


Накопившаяся разница проявляется в шестом знаке после запятой, или, иными словами, четыре последних знака потеряны.

Что делать? В общем случае - помнить об ошибках накопления и учитывать их. В конкретном случае сложения большого количества примерно одинаковых чисел самым простым оказывается складывать их "равномерно". Вместо того, чтобы одна переменная копила сумму, сложим сначала пары чисел-соседей, и получим в два раза меньше промежуточных сумм. Их тоже сложим попарно, и так далее - log(N) шагов. Пример такого алгоритма можно увидеть на этой странице (написан на Питоне).
Page 1 of 2 << [1] [2] >>

Date: 2007-08-08 07:35 pm (UTC)
From: [identity profile] vova-belkin.livejournal.com
Это еще что! Числа с плавающей точкой и сравнивать нельзя
т.е.

(4.2 - 2.2)^2 не всегда равно (5.2-3.2)^2

Факт очевидный, но на практике всегда о нем зыбываешь.

Date: 2007-08-08 07:39 pm (UTC)
From: [identity profile] avva.livejournal.com
Как раз вот этот факт я помню - и это, пожалуй, единственное, что помню о проблемах с плавающей точкой. Если бы я писал что-то нетривиальное обрабатывающее много таких чисел, до того, как прочитал об этой теме сегодня, то сравнивал бы я осторожно (сравнивая модуль разницы с подходящей маленькой константой), а вот складывал бы наивно, не задумываясь.

(no subject)

From: [identity profile] ltwood.livejournal.com - Date: 2007-08-10 08:52 pm (UTC) - Expand

(no subject)

From: [identity profile] bamsic.livejournal.com - Date: 2007-08-09 06:40 am (UTC) - Expand

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2007-08-09 08:43 am (UTC) - Expand

(no subject)

From: [identity profile] bamsic.livejournal.com - Date: 2007-08-09 08:54 am (UTC) - Expand

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2007-08-12 07:41 pm (UTC) - Expand

(no subject)

From: [identity profile] ltwood.livejournal.com - Date: 2007-08-10 08:50 pm (UTC) - Expand

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2007-08-12 07:42 pm (UTC) - Expand

(no subject)

From: [identity profile] lazyreader.livejournal.com - Date: 2007-08-12 07:06 pm (UTC) - Expand

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2007-08-12 07:39 pm (UTC) - Expand

(no subject)

From: [identity profile] lazyreader.livejournal.com - Date: 2007-08-12 08:28 pm (UTC) - Expand

(no subject)

From: [identity profile] bamsic.livejournal.com - Date: 2007-08-13 06:40 am (UTC) - Expand

(no subject)

From: [identity profile] lazyreader.livejournal.com - Date: 2007-08-13 02:36 pm (UTC) - Expand

(no subject)

From: [identity profile] 109.livejournal.com - Date: 2007-08-13 06:05 pm (UTC) - Expand

(no subject)

From: [identity profile] ltwood.livejournal.com - Date: 2007-08-13 07:56 pm (UTC) - Expand

(no subject)

From: [identity profile] ltwood.livejournal.com - Date: 2007-08-13 04:55 pm (UTC) - Expand

Date: 2007-08-08 07:36 pm (UTC)
From: [identity profile] mummy1.livejournal.com
Я очень мало знаю о числах с плавающей точкой, и меня это сердит. И на численном анализе о них ничего не расказывали.

Date: 2007-08-08 07:40 pm (UTC)
From: [identity profile] spamsink.livejournal.com
Прежде чем их складывать попарно, их еще и отсортировать полезно.

Date: 2007-08-08 07:49 pm (UTC)
From: [identity profile] eraplee.livejournal.com
а можно чуть конкретней? что даст сортировка?

(no subject)

From: [personal profile] nine_k - Date: 2007-08-08 07:59 pm (UTC) - Expand

(no subject)

From: [identity profile] dizzy57.livejournal.com - Date: 2007-08-08 08:01 pm (UTC) - Expand

Date: 2007-08-08 07:43 pm (UTC)
From: [identity profile] eraplee.livejournal.com
Мне кажется вопрос поставлен неправильно -- знал/не знал. То, что ошибка при арифметической операции будет (для очень маленьких чисел, не обязательно для всех с плавающей точкой) -- очевидно. И то, что она будет накапливаться очевидно тоже любому. Отсюда же можно легко сделать вывод, что наивный метод сложения даст довольно неточный результат. А вот как лучше сделать вместо наивного -- тут конечно уже не каждый сразу сообразит ;)

Date: 2007-08-09 06:15 am (UTC)
From: [identity profile] plakhov.livejournal.com
По-моему, проблема как раз обратная.
Очень легко написать цикл до миллиона не задумываясь.
Но как только тебе рассказали (или сам осознал), что в этом есть проблема, решение с равномерным сложением кажется почти очевидным. По крайней мере, это первое, что пришло в голову - еще до того, как я добежал глазами до конца записи.

(no subject)

From: [identity profile] avva.livejournal.com - Date: 2007-08-09 06:38 am (UTC) - Expand

Date: 2007-08-08 07:47 pm (UTC)
From: [identity profile] michk.livejournal.com
Средний выпускник действительно не знает (т.е. может учит, но всё равно не знает). Но вот если с этим делом работаешь - быстро на такие вещи наталкиваешься. Самый простой пример - арифметическая прогрессия с маленьким шагом.

Date: 2007-08-08 07:52 pm (UTC)
From: [identity profile] e2k-4d-x-ussr.livejournal.com
правильнее тогда выбирать 2 минимальных и их складвать? и так пока не останется только ОДИН. :)

Date: 2007-08-08 08:02 pm (UTC)
nine_k: A stream of colors expanding from brain (Default)
From: [personal profile] nine_k
Imho, да.
Мой первоначальный вариант -- отсортировать и складывать от меньших к большим.
Если складывать сначала наименьшие пары, получится и сложений меньше, и потому потенциальная потеря точности будет ещё меньше.

(no subject)

From: [identity profile] igorlord.livejournal.com - Date: 2007-08-08 08:26 pm (UTC) - Expand

(no subject)

From: [identity profile] dizzy57.livejournal.com - Date: 2007-08-08 08:47 pm (UTC) - Expand
(deleted comment)

Date: 2007-08-08 08:04 pm (UTC)
nine_k: A stream of colors expanding from brain (Default)
From: [personal profile] nine_k
Да можно вообще оперировать числами произвольной точности. И даже местами абсолютной, применяя рациональные дроби.
Только медленно это и места много занимает.

(no subject)

From: (Anonymous) - Date: 2007-08-12 12:30 am (UTC) - Expand

Date: 2007-08-08 07:58 pm (UTC)
From: [identity profile] akater.livejournal.com
Я вообще не выпускник факультета и не CS-ник. Однако я знаю об этом (читал в своё время древнюю, но всё равно полезную книгу под названием "Компьютерная алгебра", автора сейчас не вспомню), и считаю, что при моей специальности ("прикладная математика") не знать этого ещё хуже, чем при CS.

Более того, имхо, это проблема тех, кто преподаёт как раз математику, а не CS. Если курсы анализа и численных методов (в т.ч. компьютерных вычислений) не сопрягать, всё будет очень плохо.

Date: 2007-08-08 08:04 pm (UTC)
From: [identity profile] igorlord.livejournal.com
"и так далее - log(N) шагов"

There are still N-1 steps. :)

It's just that the "outer loop" of the algorithm runs log(N) times.

Date: 2007-08-08 11:12 pm (UTC)
From: [identity profile] malaya-zemlya.livejournal.com
Там еще сравнения...

(no subject)

From: [identity profile] avva.livejournal.com - Date: 2007-08-09 05:13 am (UTC) - Expand

Date: 2007-08-08 08:04 pm (UTC)
From: [identity profile] ygam.livejournal.com
По-моему, это описывается в книжке по численному анализу в серии McGraw Hill International Series on Pure and Applied Mathematics; она 13 лет назад была в майкрософтовской корпоративной библиотеке.

Date: 2007-08-08 08:06 pm (UTC)
From: [identity profile] ygam.livejournal.com
http://www.emis.de/journals/NNJ/DavIns-HM.html

Does anyone believe that the difference between the Lebesgue and Riemann integrals can have physical significance, and that whether, say, an airplane would or would not fly could depend on this difference? If such were claimed, I should not care to fly in that plane. --Richard W. Hamming

Date: 2007-08-08 08:08 pm (UTC)

Date: 2007-08-08 08:28 pm (UTC)
From: [identity profile] amarao-san.livejournal.com
Вас плохо сношали в моск. Первое что нам на вычмате в голову вбили, что "математика компьютера это не математика". И десять лабораторных работ, в которых вместо ответа получалось фиг знает что, пока не "примешь меры".

Date: 2007-08-08 08:29 pm (UTC)
From: [identity profile] vrml.livejournal.com
На физтехе на численных методах рассказывали.

Date: 2007-08-09 10:44 am (UTC)
From: [identity profile] http://users.livejournal.com/malfet_/
+1
А кто был лектором, если не секрет? Не Чудов Л.А.?

(no subject)

From: [identity profile] vrml.livejournal.com - Date: 2007-08-09 08:57 pm (UTC) - Expand

Date: 2007-08-08 08:41 pm (UTC)
From: [identity profile] talash.livejournal.com
Not a computer science graduate or student as of today, but the issue is not new to me. Programmers of the patriot missile defense system should have known better though (http://www.ima.umn.edu/~arnold/disasters/patriot.html)

Date: 2007-08-09 04:00 am (UTC)
From: [identity profile] dr-tambowsky.livejournal.com
Из чистого занудства, но что то там не то. Задача системы - отслеживать цель. Соответственно, смещение окна - это скорость цели, помноженная на (очень небольшое) время, прошедшее с момента последнего вычисления окна. Как они сами пишут: "The range gate's prediction of where the Scud will next appear is a function of the Scud's known velocity and the time of the last radar detection."

Насколько у системы отстали часы - совершенно не важно, важен только временной интервал. Как-то я не могу себе представить, чтобы ракету детектировали при запуске системы, а потом ждали 100 часов, чтобы вычислить новое положение окна - в этом случае да, 0.34 секунды, заработанные за 100 часов, сыграли бы свою роль... Там, видимо, другая проблема - с преобразованием: например, вместо того, чтобы вычислить разность показаний таймера в исходном точном представлении (целых числах), а потом конвертировать маленькую разность в плавающую точку, ребята сначала конвертировали близкие начальное и конечное показания, потом посчитали разность уже в плавающей арифметике. Когда система работает долго, и начальное и конечное показания - большие числа. Конвертировать и затем вычитать их на таком коротком регистре - действительно некрасиво, маленькая разность двух больших плавающих чисел утонет в погрешности. Из официального отчёта, вроде бы, следует, что так оно и было. Объяснение про 0.34 секунды - абсолютно неправильное.

Но так или иначе, история очень поучительная :)

Date: 2007-08-08 08:45 pm (UTC)
From: [identity profile] spamsink.livejournal.com
Реальные примеры различия плавающих точек на разных архитектурах приводились в брошюре "Ошибки и ловушки при программировании на фортране" из серии "Библиотечка программиста". В качестве разных архитектур брались ЕС ЭВМ и БЭСМ-6. Первая проигрывала в длине мантиссы (24 против 40), а последняя - в диапазоне порядков (+/- 19 против +/- 38), так что нужно было заботиться и о потере значащих цифр - не только при сложении далеких чисел, но и при вычитании близких, и о пере-недо-полнениях в промежуточных результатах.

Date: 2007-08-08 08:52 pm (UTC)
From: [identity profile] dmpogo.livejournal.com
+1

Dlja teh kto zanimaetsja chislennymi raschetami, skzazhem nauchnnymi, eto nachal'nye bazowye znanija. Ljuboj malo-malo prilichnyj chislennyj algoritm zabotitsja o wozmozhnoj poteri tochnosti.

(no subject)

From: [identity profile] spamsink.livejournal.com - Date: 2007-08-08 09:38 pm (UTC) - Expand

(no subject)

From: [identity profile] dmpogo.livejournal.com - Date: 2007-08-08 10:39 pm (UTC) - Expand

(no subject)

From: [identity profile] ltwood.livejournal.com - Date: 2007-08-10 09:00 pm (UTC) - Expand

Date: 2007-08-08 08:52 pm (UTC)
From: [identity profile] e2k-4d-x-ussr.livejournal.com
как вариант 2:
написать математику сложения произвольных целых, записанных в виде строк. сконвертировать даблы в безразмерные целые (не более 1000 знаков) и поскладывать.

С умножением и делением сложнее.

Date: 2007-08-09 02:02 am (UTC)
From: [identity profile] mivlad.livejournal.com
Да есть такие библиотеки, даже и с умножением и делением и прочим.

Только вот милион чисел таким образом обрабатывать займёт довольно много времени.

(no subject)

From: [identity profile] e2k-4d-x-ussr.livejournal.com - Date: 2007-08-09 07:09 am (UTC) - Expand

Re: Reply to your comment...

From: [identity profile] mivlad.livejournal.com - Date: 2007-08-09 07:31 am (UTC) - Expand

Date: 2007-08-08 10:43 pm (UTC)
From: [identity profile] gaius-julius.livejournal.com
я не помню чтобы меня этому учили, но несколько лет назад столкнулся с такой особенностью и поэтому знаю.

вообще вещь на первый взгляд довольно простая и очевидная, но такие мелочи запоминаются только когда встречаются в реальной жизни.

Date: 2007-08-09 01:10 am (UTC)
From: [identity profile] azzo27.livejournal.com
Про это еще в старинной блестящей книжке Мак-Кракена "Численные методы и программирование на ФОРТРАНе" было.
Надо сначала отсортировать по модулю, а потом складывать, начиная от меньших. Тогда точность была меньше и это было более актуально.

Date: 2007-08-09 05:18 am (UTC)
From: [identity profile] itman.livejournal.com
Я знал, потому что мне задали такой вопрос на собеседовании лет десять назад. Правда, там было в контексте вычисления среднего арифметического, а со средним арифметическим проще бороться.

Date: 2007-08-09 07:45 am (UTC)
From: [identity profile] fantaseour.livejournal.com
Со средним там и всякие медианы приходится делать :)

А мне помнится как достопамятный Златкис из УПЦ приводил пример, что нельзя считать Cpn в лоб просто посчитав факториалы -- переполнение наступит очень быстро.

(no subject)

From: [identity profile] itman.livejournal.com - Date: 2007-08-09 07:54 am (UTC) - Expand

Date: 2007-08-09 05:39 am (UTC)
From: [identity profile] os80.livejournal.com
Я CS в институте не изучал, но когда знаешь, как устроены компьютерные действительные числа и что такое "машинное эпсилон", догадаться нетрудно. Проблема в том, как оценить, насколько важны такие эффекты в контексте данной задачи.

Date: 2007-08-09 05:46 am (UTC)
From: (Anonymous)
Вместо того, чтобы одна переменная копила сумму, сложим сначала пары чисел-соседей, и получим в два раза меньше промежуточных сумм.

Это не очень эффективно. Есть способ, требующий O(log N) дополнительной памяти: в одном сумматоре копится сумма не более чем двух элементов последовательности, в следующем сумматоре - сумма не более чем двух предыдущих сумматоров и т. д. Работает это как-то так:

0. S1 = data[0];
1. S1 += data[1] (счетчик наполнился, переносим выше); S2 = S1; S1 = 0;
2. S1 = data[2];
3. S1 += data[3] (счетчик наполнился, переносим выше); S2 += S1 (и этот счетчик наполнился); S3 = S2; S2 = 0; S1 = 0;
и так далее.

Какие именно операции с какими счетчиками надо делать, легко определить по значению соответствующей цифры в двоичной записи номера шага.

Date: 2007-08-09 05:50 am (UTC)
From: [identity profile] avva.livejournal.com
Неплохо, да.

Кроме того, есть и простой линейный способ суммирования, не требующий дополнительной памяти, и эффективно снижающий ошибку - в статье, которая раньше тут упоминалась в комментах, о нем говорится (theorem 8).

(no subject)

From: (Anonymous) - Date: 2007-08-09 06:30 am (UTC) - Expand

(no subject)

From: (Anonymous) - Date: 2007-08-09 06:57 am (UTC) - Expand

Date: 2007-08-09 06:11 am (UTC)
From: [identity profile] http://users.livejournal.com/_kulikov/
У нас это на третьем курсе изучают в рамках численных методов.

Date: 2007-08-09 07:05 am (UTC)
From: [identity profile] gt.livejournal.com
Большинство программистов неправильно сравнивают числа с плавающей запятой - полно примеров в опен сорсе. Введение в правильные способы: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

Date: 2007-08-09 07:45 am (UTC)
From: [identity profile] mopexod.livejournal.com
Да, и скалярное произведение векторов деленное на произведение их модулей иногда >1, и arcsin падает.
И школьная формула квадратного уравнения на практике плоха или даже совсем не работает.
А уж что происходит при решении "неудобной" линейной системы уравнений с помощью numerical recipes - смотреть страшно.

Date: 2007-08-10 09:19 pm (UTC)
From: [identity profile] ltwood.livejournal.com
Проблема с арксинусом относится к историческим курьезам (оно было так на одной из серий машины Cray), корректная реализация стандарта IEEE-754 гарантирует в этом случае правильное поведение и для бесхитростной реализации.

Наезд на NR соврешенно беспочвенный, там реализован т.н. алгоритм GEPP т.е. LU-факторизация Гаусса с частичным выбором ведущего элемента. Для матриц общего вида этот метод применяется везде и всюду, не только в NR. Проблемы возникают для слабо обусловленных систем, но слабая обусловленность является свойством задачи и проблемой того недальновидного вычислителя, который не проверил предварительно обусловленность системы.

(no subject)

From: [identity profile] mopexod.livejournal.com - Date: 2007-08-12 06:37 am (UTC) - Expand
Page 1 of 2 << [1] [2] >>

December 2025

S M T W T F S
  123 4 56
78 9 10 11 1213
1415 1617181920
21 22 23 24 2526 27
28293031   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Dec. 28th, 2025 10:06 am
Powered by Dreamwidth Studios