OSDev

для всех
Текущее время: 28 мар 2024, 19:56

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 44 ]  На страницу Пред.  1, 2, 3, 4, 5  След.
Автор Сообщение
СообщениеДобавлено: 23 мар 2015, 13:34 
Аватара пользователя

Зарегистрирован: 28 май 2012, 23:44
Сообщения: 237
Откуда: Санкт-Петербург
Yoda писал(а):
Интересная бодяга, почему компилятор GCC настойчиво считает не как остальные.

Он считает как Delphi. В обычном Delphi нельзя воспроизвести результат с умолчательным контрольным словом сопроцессора для процесса Windows, только с хакерским модулем System от проекта KOL, как у меня. Поэтому стандартным для Delphi является результат с контрольным словом $1372, который как раз равен 1.2875059979e+26.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 23 мар 2015, 15:00 
Аватара пользователя

Зарегистрирован: 17 фев 2013, 16:13
Сообщения: 163
В целом-то понятно, почему результат будет близок к 10^26, ведь double обеспечивает около 15 значащих цифр, а в результате некоторых действий их становится уже 14. От есть 40-14=26. Таким образом, 10^26 - это относительная погрешность для чисел порядка 10^40 и тех арифметических действий, что мы делаем. Собственно, ответ в примере Yoda действительно будет 0 с погрешностью около 10^26 :)

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

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

Видимо, мне придётся на эту тему сделать какой-то основательный обзор... А то как работать с математической библиотекой без понимания тонкостей плавающей арифметики.

Цитата:
В данной формулировке получается, что VAX лишён этой проблемы, верно?

Как я понял, там арифметика с фиксированной точкой, которая позволяет использовать абсолютную погрешность на все случаи. Эта арифметика более очевидна и если я, скажем, хочу прибавить к 10^20 число 1 (один) миллиард раз, я получу то, что должен был получить, а не то же самое 10^20.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 23 мар 2015, 15:41 
Аватара пользователя

Зарегистрирован: 28 май 2012, 23:44
Сообщения: 237
Откуда: Санкт-Петербург
Я ночью полез было смотреть инструкции SSE, чтобы написать этот кусок на ассемблере, но в этот раз бессонница была не такой жестокой.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 23 мар 2015, 20:48 
Аватара пользователя

Зарегистрирован: 17 фев 2013, 16:13
Сообщения: 163
А чём поможет SSE? От того, что удастся сразу два double за раз умножить вряд ли результат сильно поменяется.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 23 мар 2015, 21:08 
Аватара пользователя

Зарегистрирован: 28 май 2012, 23:44
Сообщения: 237
Откуда: Санкт-Петербург
Zealint писал(а):
Таким образом, 10^26 - это относительная погрешность для чисел порядка 10^40 и тех арифметических действий, что мы делаем. Собственно, ответ в примере Yoda действительно будет 0 с погрешностью около 10^26 :)

Чёрт возьми! Тогда результат 0,0, полученный мной для 80-битного типа Extended, верен для общего случая, а установка слова сопроцессора нужна Delphi как раз для повышенной точности. GCC как, умеет работать с 80-битным типом?

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

Zealint писал(а):
А чём поможет SSE? От того, что удастся сразу два double за раз умножить вряд ли результат сильно поменяется.

А в каком-то из SSE разве не было 128-битных плавающих значений? Я до этого пока не дошел. Знаю только, что 80-битные в x64 выпилили.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 23 мар 2015, 21:27 
Аватара пользователя

Зарегистрирован: 17 фев 2013, 16:13
Сообщения: 163
Freeman писал(а):
А вы сами себе противоречите, сначала говоря, что погрешность рядом не лежала с объявленной для double, а теперь признавая обратное.

Не совсем так. Я сказал
Цитата:
что вычисления даже близко не подходят к границам применения double

То есть мы не вышли за пределы применения типа данных (1.7e308), но точность в привычном для человека смысле потеряли. Согласитесь, трудно считать число 10^26 почти нулём : ) И похожая неочевидность порождают очень много ошибок при работе с плавающей арифметикой, говорят, даже ракеты падают из-за этого. Вот представьте, что Вы бы попались на это в реальном проекте: вычитаете два одинаковых числа, а у вас получается сто миллионов миллиардов миллиардов! Начнёте отлаживать, напьётесь зелёного чая, кто-то другой на Вашем месте может и вовсе в обморок упасть от ужаса. И это ещё полбеды. Беда приходит, когда от этого умирают (и о таких случаях тоже ходят слухи) - когда ошибка становится результатом аварии со смертельным исходом. Вот я о чём - о непредсказуемости и неочевидности плавающей арифметики. И я ещё не начал даже разговора о более суровых примерах, когда мы вроде бы маленькие числа считаем, а в конце получается фигня. Я поищу, может, эти примеры, где-то их видел раньше.

Freeman писал(а):
А в каком-то из SSE разве не было 128-битных плавающих значений?

Насколько я знаю, нет. Смысл SSE (Streaming SIMD Extensions) как раз в том, чтобы те же операции делать в потоковом режиме: сразу много за один раз. Например, можно перемножить сразу 4 float за раз, или 2 double. Это ускоряет, например, задачу перемножения матриц, когда нужно брать длинные суммы произведений, но это не повышает точность расчётов.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 23 мар 2015, 21:57 
Аватара пользователя

Зарегистрирован: 17 фев 2013, 16:13
Сообщения: 163
Вот, кстати, более интересные ошибки можно поискать здесь. Ищите там сразу
§9. Проблемы компьютерных вычислений, вызванные использованием стандарта IEEE754.
Десятый параграф тоже можно прочитать, но я бы критично его оценил.

Разумеется, все эти вещи - это ошибки человека. Но извольте, трудно управлять неконтролируемой погрешностью. Особенно когда о ней ничего не знаешь. Бывает, читаешь код, написанный профессионалом, а там написано
Код:
if ( abs ( a - b ) < EPS ) { ... }

Причём EPS указана в виде константы, заданной заранее на все случаи. Например, часто берут 1e-8. А когда выясняется, что 10^26 тоже может быть нулём... в общем, понимаете? Люди в целом плохо знают плавающую арифметику, раз даже профессионалы совершают очевидные ошибки. Но я вас уверяю, даже если бы они знали всё это, вероятность ошибок была бы всё равно в десятки раз выше, чем при работе с целыми числами. Я лично считаю плавающую арифметику плохим инструментом.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 24 мар 2015, 08:45 

Зарегистрирован: 28 окт 2007, 18:33
Сообщения: 1418
Zealint писал(а):
Я лично считаю плавающую арифметику плохим инструментом.


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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 24 мар 2015, 13:22 
Аватара пользователя

Зарегистрирован: 14 мар 2011, 12:31
Сообщения: 970
Откуда: Дагоба
Zealint писал(а):
Прикол в том, что разные компиляторы могут по разному генерировать код вычислений. Поменяв порядок действий, мы можем получить совершенно разные ответы.

Именно по этой причине на компиляторы обычно накладываются ограничения в работе с плавающей арифметикой. Так, они не имеют права оптимизировать (без специальных на то указаний) плавающую арифметику, меняя порядок операций.

Zealint писал(а):
Видимо, мне придётся на эту тему сделать какой-то основательный обзор...

Вот тот обзор на хабре (упомянутый мной ранее) мне показался вполне обзорным.

Zealint писал(а):
Yoda писал(а):
В данной формулировке получается, что VAX лишён этой проблемы, верно?

Как я понял, там арифметика с фиксированной точкой, которая позволяет использовать абсолютную погрешность на все случаи. Эта арифметика более очевидна и если я, скажем, хочу прибавить к 10^20 число 1 (один) миллиард раз, я получу то, что должен был получить, а не то же самое 10^20.

С чего бы вдруг? По части представления чисел ваксы в основном похожи на IEEE: http://nssdc.gsfc.nasa.gov/nssdc/format ... gPoint.htm
Никакой фиксированной точки там нет. Также там нет и ряда других концепций, таких как специальные и денормализованные числа, поэтому ваксы подвержены ряду проблем: "околонулевая яма", неправомерная выдача правдоподобного результата, -0 != +0 и т.п. Собс-но, прелесть ваксов заключалась, пожалуй, только в 128-битной арифметике при наличии специального процессорного модуля расширения.
Опять же, я не вижу способа решить эту проблему полностью, она носит фундаментальный характер. В любом представлении с фиксированным количеством значащих разрядов мы либо ограничиваем диапазон, либо ограничиваем точность, но не можем получить и то и другое сразу. Число 10^40 требует ceil(40/log(2))-40 = 93 двоичных разряда мантиссы для хранения без потери, что сильно превышает возможности 80-битных чисел с плавающей точкой В ЛЮБОМ представлении, а ещё немного и превысит даже возможности расширенных ваксов. Главная мысль - плавающая арифметика имеет неразрешимые внутренние проблемы (не важно, какое представление мы выбрали), специалисту их необходимо постоянно держать в голове и учитывать при написании программ.

Zealint писал(а):
Бывает, читаешь код, написанный профессионалом, а там написано
Код:
if ( abs ( a - b ) < EPS ) { ... }

Да-да, ужасная, КАТАСТРОФИЧЕСКАЯ ошибка.

Zealint писал(а):
Я лично считаю плавающую арифметику плохим инструментом.

Уж какой есть. За невозможностью разработать лучший для ряда задач приходится пользоваться этим. Важно понимать, что можно от неё ожидать, а что - нет, и не применять её там, где вреда больше, чем пользы.

_________________
Yet Other Developer of Architecture.
The mistery of Yoda’s speech uncovered is:
Just an old Forth programmer Yoda was.

<<< OS Boot Tools. >>>


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 24 мар 2015, 23:34 
Аватара пользователя

Зарегистрирован: 28 май 2012, 23:44
Сообщения: 237
Откуда: Санкт-Петербург
Yoda писал(а):
Опять же, я не вижу способа решить эту проблему полностью, она носит фундаментальный характер. В любом представлении с фиксированным количеством значащих разрядов мы либо ограничиваем диапазон, либо ограничиваем точность, но не можем получить и то и другое сразу.

По одной из ссылок про страшилки IEEE754 есть статья про какую-то аппроксиметику, которую я смотрел только по диагонали. Преподносится автором как серебряная пуля, само собой. Если кто-то сможет проанализировать и сказать, годная идея или нет, буду только рад.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 44 ]  На страницу Пред.  1, 2, 3, 4, 5  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB