Числа с плавающей точкой
От: FrozenHeart  
Дата: 06.04.16 20:13
Оценка:
Блин, сложная же тема эти числа с плавающей точкой. По крайней мере, для меня.

Вот есть binary floating point numbers, т.е. числа с плавающей точкой, у которых base == 2.
Все мы прекрасно знаем, какие проблемы они могут сулить в руках неопытного разработчика:
— Т.к. они обладают ограниченной точностью, при выполнении арифметических операций может постепенно накапливаться ошибка из-за выполняемых округлений
— Некоторые числа не могут быть представлены вовсе (например, какие-нибудь 0.1, как описано тут, или 1/3)
— Как результат предыдущих пунктов, сравнение нельзя производить напрямую через operator==, его надо осуществлять при помощи epsilon
— ???

Есть такая штука, как decimal floating point numbers.
Они, согласно вики, позволяют избегать ошибок округления, связанных с конвертацией между десятичным и двоичным представлением, т.е. какие-нибудь 0.1 там уже представить можно.
Чего там сделать нельзя, так это, насколько я понимаю, избежать ситуации с числами наподобие 1/3 и всё теми же же ошибками округления, связанными с ограниченным precision'ом (да, он хоть и больше, чем у binary floating point numbers, но всё же имеется).

Есть ещё fixed point numbers, которые, если говорить грубо, просто состоят из двух целочисленных значений -- одно для целой части, а другое для дробной, причём дробная часть имеет фиксированное кол-во знаков после запятой.

В общем, суть такова.

Имеется:
— Приложение, написанное на C++ и использующее фреймворк, из которого ко мне в код попадают переменные типа double
— Доступа к исходному коду данного фреймворка у меня нет, изменять я его не могу
— В приложении выполняются разнообразные операции над числами с плавающей точкой, после чего они возвращаются обратно во фреймворк

Хочется:
— Минимизировать кол-во ошибок, которые можно допустить при работе с дробными числами в данном случае

Кол-во значащих цифр после запятой мне известно, ожидаемый фреймворком алгоритм округления в случае превышения precision'а тоже.
Что в таком случае посоветуете сделать?

— Продолжать использовать double'ы, не забывая о корректном сравнении и прочих подводных камнях
— Начать использовать decimal floating point numbers и получить преимущество (?). Сравнивать их через operator== без epsilon можно, кстати?
— Начать использовать fixed point numbers и получить преимущество (?)
— Переводить double'ы в int'ы, выполнять над ними все необходимые операции и конвертировать обратно в double для передачи фреймворку?
— ???

Любые комментарии и советы приветствуются, буду признателен за ваши ответы.
Re: Числа с плавающей точкой
От: watchmaker  
Дата: 06.04.16 22:56
Оценка: 8 (2) +2
Здравствуйте, FrozenHeart, Вы писали:

FH>- В приложении выполняются разнообразные операции над числами с плавающей точкой, после чего они возвращаются обратно во фреймворк

FH>Хочется:
FH>- Минимизировать кол-во ошибок, которые можно допустить при работе с дробными числами в данном случае

Наверное, самого главного не написал — что это за самые "разнообразные операции"? От их вида и будет зависеть какое из решений окажется лучше. Без этого остальные рассуждения будут носить слегка абстрактный характер :)

FH>- Продолжать использовать double'ы, не забывая о корректном сравнении и прочих подводных камнях

Если точность устраивает, то отличный вариант. Быстрый и простой. Конечно, получаемая точность будет зависеть и от последовательности "разнообразных операций" и от самого распределения входных данных. Не зная этого ничего верный ответ не дать. Нужно самому в конкретной задаче оценить погрешности. При этом также не стоит забывать, что иногда погрешности можно значительно нивелировать незначительными изменениями алгоритма (вроде применения суммирования Кэхэна) даже оставаясь в рамках типа double.

FH>- Начать использовать decimal floating point numbers и получить преимущество (?).

Ну это явный аутсайдер.
Как верно заметил, decimal floating point позволяет избежать сложностей при округлении при конвертации между десятичным и двоичным представлениями. Если на входе и выходе у тебя уже и так тип double зафиксирован, то это единственно преимущество тут же уничтожается.
Выгода будет только в очень редких случаях, когда, например, пол-алгоритма будет занимать операция деления на число 5. Тогда ради её упрощения и можно к десятичному основанию перейти.
FH> Сравнивать их через operator== без epsilon можно, кстати?
Конечно можно. Ровно с таким же смыслом, как и обычные двоичные floating point числа :)

FH>- Начать использовать fixed point numbers и получить преимущество (?)

Опять же, зависит от операций и самих чисел. Для fixed-point, например, очень тяжело даётся операция ((x / basek) * basek) , которая вообще не представляет проблем для floating point (base=2 для binary floating, base=10 для decimal, k — натуральное). Есть среди твоих операций операция деления? На какие числа? Вот из этих соображений и решать вопрос о применимости fixed-point.

FH>- Переводить double'ы в int'ы, выполнять над ними все необходимые операции и конвертировать обратно в double для передачи фреймворку?

А на низком уровне double что-ли не так процессорами и эмуляторами обрабатываются? От того, что тоже самое будет сделано будет вручную, точность магически не появится.
Вот заменить double на quadruple для промежуточных расчётов — уже получше вариант (опять же не важно, как это будет реализовано — аппаратно или вручную "через int'ы"). Так по крайней мере можно легко часть погрешностей перенести в младшие биты длинного числа, которые потом не помешают при возврате к double. Ну или помешают и нужно будет octuple брать :)
Ну и конечно, не самому это писать, а библиотеку вроде MPFR использовать. И не забывать, что даже поставив огромную длину и точности для чисел, всё равно можно придумать последовательность действий, для которых её не будет достаточно.
Re: Числа с плавающей точкой
От: pagid Россия  
Дата: 07.04.16 05:04
Оценка:
Здравствуйте, FrozenHeart, Вы писали:

FH>Есть такая штука, как decimal floating point numbers.

FH>Они, согласно вики, позволяют избегать ошибок округления, связанных с конвертацией между десятичным и двоичным представлением, т.е. какие-нибудь 0.1 там уже представить можно.
FH>Чего там сделать нельзя, так это, насколько я понимаю, избежать ситуации с числами наподобие 1/3 и всё теми же же ошибками округления, связанными с ограниченным precision'ом (да, он хоть и больше, чем у binary floating point numbers, но всё же имеется).
Им присущи все вышеперечисленные для binary floating point numbers недостатки.
— Они обладают ограниченной точностью, при выполнении арифметических операций может постепенно накапливаться ошибка из-за выполняемых округлений
— Некоторые числа не могут быть представлены вовсе (например, какие-нибудь 0.3(3), как описано выше
— Как результат предыдущих пунктов, сравнение нельзя производить напрямую через operator==, его надо осуществлять при помощи epsilon
Преимущество тоже описал — "позволяют избегать ошибок округления, связанных с конвертацией между десятичным и двоичным представлением". Но про недостаток — меньшая скорость обработки, забыл.


FH>В общем, суть такова.


FH>Имеется:

FH>- Приложение, написанное на C++ и использующее фреймворк, из которого ко мне в код попадают переменные типа double
FH>Любые комментарии и советы приветствуются, буду признателен за ваши ответы.

Без знания области применения сложно что-то посоветовать.
Но если работа с тем фреймворком составляет существенную часть логики программы, вероятно использование double очень неплохой вариант, или лучший, или единственно правильный. Но это как раз от назначения программы и характера расчетов зависит.
Отредактировано 07.04.2016 5:20 pagid . Предыдущая версия .
Re[2]: Числа с плавающей точкой
От: FrozenHeart  
Дата: 07.04.16 06:30
Оценка:
W>Наверное, самого главного не написал — что это за самые "разнообразные операции"?
Не написал конкретику из-за того, что в общем случае вычисления могут быть абсолютно любыми -- грубо говоря, программа выполняет расчёты согласно формулам, занесённым пользователем в конфигурационный файл. Т.е. это может быть и сложение, и вычитание, и умножение, и деление.

W>Конечно можно. Ровно с таким же смыслом, как и обычные двоичные floating point числа

Я думал, что из-за того, что здесь любое "finite" число представляется однозначно, без "искажений", это вполне возможно.

Ну, т.е. если мы используем binary floating point numbers, то, понятное дело, следующий код -- это фейл:

if (number == 0.1) { /* do smth */ }


Однако в случае decimal'ов все "finite" числа представляются точно, без "искажений" и округлений, разве нет?

W>Есть среди твоих операций операция деления? На какие числа? Вот из этих соображений и решать вопрос о применимости fixed-point.

Ответил ранее

W>А на низком уровне double что-ли не так процессорами и эмуляторами обрабатываются? От того, что тоже самое будет сделано будет вручную, точность магически не появится.

Почему же? Взять, например, всё то же число 0.1.
Если мы оперируем им в случае binary floating point numbers, то следующий цикл запросто накопит существенную ошибку:

for (int i = 0; i < 10000000; ++i)
{
  number += 0.1;
}


Однако если бы мы сразу перевели все дробные значения в int'ы, то ошибка была бы только в моменты конвертации, а сложение происходило бы без проблем:

int int_number = static_cast<int>(number * 100); // Допустим, нам важен precision == 2
for (int i = 0; i < 10000000; ++i)
{
  int_numbernumber += static_cast<int>(0.1 * 100);
}


Я, может быть, туплю?
Re[2]: Числа с плавающей точкой
От: FrozenHeart  
Дата: 07.04.16 06:33
Оценка:
P>Преимущество тоже описал — "позволяют избегать ошибок округления, связанных с конвертацией между десятичным и двоичным представлением". Но про недостаток — меньшая скорость обработки, забыл.
Вы точно про binary floating point numbers говорите? Может, имелись ввиду decimal'ы? Цитата вроде неправильная.

P>Без знания области применения сложно что-то посоветовать.

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

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

Да, операции над ценами выполняются довольно часто.
Re[3]: Числа с плавающей точкой
От: FrozenHeart  
Дата: 07.04.16 06:46
Оценка:
FH>Ну, т.е. если мы используем binary floating point numbers, то, понятное дело, следующий код -- это фейл:

FH>
FH>if (number == 0.1) { /* do smth */ }
FH>


FH>Однако в случае decimal'ов все "finite" числа представляются точно, без "искажений" и округлений, разве нет?


Блин, сказал, как идиот.

Если number в приведённом примере изначально присваивается 0.1 и над ним не производится никаких арифметических операций, то сравнение number == 0.1 и в случае binary floating point numbers сработает отлично.

Скорее, пример должен быть такой:

double number = 0.1 + 0.1 + 0.1;

if (numbers == 0.3) { /* do smth */ }


Вот такие ситуации в случае decimal floating point numbers разве могут когда-то зафейлиться?
Re: Числа с плавающей точкой
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 07.04.16 06:48
Оценка:
Здравствуйте, FrozenHeart, Вы писали:

FH>- Приложение, написанное на C++ и использующее фреймворк, из которого ко мне в код попадают переменные типа double

FH>- Доступа к исходному коду данного фреймворка у меня нет, изменять я его не могу

Что известно про числа и задачу? Матфизика, финансы, что-то другое?

FH>- В приложении выполняются разнообразные операции над числами с плавающей точкой, после чего они возвращаются обратно во фреймворк

FH>Хочется:
FH>- Минимизировать кол-во ошибок, которые можно допустить при работе с дробными числами в данном случае
FH>Кол-во значащих цифр после запятой мне известно, ожидаемый фреймворком алгоритм округления в случае превышения precision'а тоже.
FH>Что в таком случае посоветуете сделать?
FH>- Продолжать использовать double'ы, не забывая о корректном сравнении и прочих подводных камнях

В общем случае — да, остаться на привычных binary floats.
Причина очень проста: base=10 ничего положительного тебе не даст (у тебя уже на входе и выходе binary), и они хуже потому, что с ростом base увеличиваются ошибки округления и дребезг значения младшего разряда.

Decimal floats имеют смысл только в тех случаях, когда числа гарантированно представляются как десятичная дробь (грубо говоря, это финансы), а где не представляются — сразу округляются после вычисления до нужной точности.

FH>- Начать использовать decimal floating point numbers и получить преимущество (?). Сравнивать их через operator== без epsilon можно, кстати?


Преимущества нет. Сравнивать тоже нельзя, кроме случаев, когда расчёты гарантированно ограничены стандартными финансовыми операциями, типа 4 арифметики и проценты.

FH>- Начать использовать fixed point numbers и получить преимущество (?)


Если это финансы, да, так лучше. Иначе — см. выше.
The God is real, unless declared integer.
Re[3]: Числа с плавающей точкой
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 07.04.16 06:50
Оценка: +1
Здравствуйте, FrozenHeart, Вы писали:

W>>Конечно можно. Ровно с таким же смыслом, как и обычные двоичные floating point числа

FH>Я думал, что из-за того, что здесь любое "finite" число представляется однозначно, без "искажений", это вполне возможно.

Точно также 1/3 в десятичке не будет точным. И, как уже сказал, дребезг младшего разряда и погрешность округления растёт с ростом base.

FH>Однако в случае decimal'ов все "finite" числа представляются точно, без "искажений" и округлений, разве нет?


Нет. Только конечные десятичные дроби. Ваш кэп.
The God is real, unless declared integer.
Re[2]: Числа с плавающей точкой
От: FrozenHeart  
Дата: 07.04.16 06:53
Оценка:
N>Что известно про числа и задачу? Матфизика, финансы, что-то другое?
Ответил рядом. Предметная область -- финансы.
Re[3]: Числа с плавающей точкой
От: pagid Россия  
Дата: 07.04.16 06:55
Оценка:
Здравствуйте, FrozenHeart, Вы писали:

FH>Вы точно про binary floating point numbers говорите? Может, имелись ввиду decimal'ы? Цитата вроде неправильная.

Сравниваю binary floating point numbers и decimal floating point numbers. И не вижу между ними особой разницы. Но нюансы есть.

FH>Область применения -- финансовая сфера (да, и при этом фреймворк подсовывает double для хранения цен).

Фреймворк изначально предназначен для финасовых расчетов?

FH>Операции могут быть самими разнообразными, ответил рядом -- грубо говоря, программа выполняет расчёты согласно формулам, занесённым пользователем в конфигурационный файл. Т.е. это может быть и сложение, и вычитание, и умножение, и деление.

Но только финасовые?
В фреймворк что передается, эти формулы и числовые данные, а возвращается результат расчета? Что с ним нужно сделать — показать пользователю, записать в БД или отправить в следущий по цепочке расчет.


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

FH>Да, операции над ценами выполняются довольно часто.
Почему имеено над ценами?

Результат расчетов это денежные суммы с точностью до целых копеек(центов) или что-то другое?
... << RSDN@Home 1.2.0 alpha 5 rev. 1495>>
Re[4]: Числа с плавающей точкой
От: FrozenHeart  
Дата: 07.04.16 06:57
Оценка:
N>Нет. Только конечные десятичные дроби. Ваш кэп.
Но ведь неконечные хоть в каком виде представь -- они всё равно не могут быть отражены точно (тот же случай с 1/3).
Re[4]: Числа с плавающей точкой
От: FrozenHeart  
Дата: 07.04.16 07:00
Оценка:
FH>>Область применения -- финансовая сфера (да, и при этом фреймворк подсовывает double для хранения цен).
P>Фреймворк изначально предназначен для финасовых расчетов?
Фреймворк передаёт в мою программу информацию о проведённых операциях. Помимо всей остальной информации, там имеются и цены. Мне необходимо провести над ними некоторые манипуляции в виде арифметических операций и отдать обратно фреймворку, который уже засунет результат в кучу разных мест -- свою внутреннюю БД, в терминал клиентов и т.д.

P>Результат расчетов это денежные суммы с точностью до целых копеек(центов)

Да.
Re[5]: Числа с плавающей точкой
От: pagid Россия  
Дата: 07.04.16 07:14
Оценка: 3 (1)
Здравствуйте, FrozenHeart, Вы писали:

FH>Фреймворк передаёт в мою программу информацию о проведённых операциях. Помимо всей остальной информации, там имеются и цены. Мне необходимо провести над ними некоторые манипуляции в виде арифметических операций и отдать обратно фреймворку, который уже засунет результат в кучу разных мест -- свою внутреннюю БД, в терминал клиентов и т.д.

Работай с double.
Если твои вычисления настолько сложны и многоэтапны, что могут воникнуть погрешности связанные с вычислениями, а не представлением чисел, можно делать округление перед возвратом значений в фреймворк. Возможно округление нужно и для каких-то промежуточных результатов, но это зависит от задачи. Обрати внимание на ситуации, когда сумма каких-то показателей полученных расчетным путём должна быть равна какой-то фиксированной сумме, возможно придется где-то накидывать/убирать копейку, это распространенная проблема, но со способом представления чисел она тоже не связана.
... << RSDN@Home 1.2.0 alpha 5 rev. 1495>>
Re[6]: Числа с плавающей точкой
От: FrozenHeart  
Дата: 07.04.16 08:21
Оценка:
P>Работай с double.
P>Если твои вычисления настолько сложны и многоэтапны, что могут воникнуть погрешности связанные с вычислениями, а не представлением чисел, можно делать округление перед возвратом значений в фреймворк. Возможно округление нужно и для каких-то промежуточных результатов, но это зависит от задачи. Обрати внимание на ситуации, когда сумма каких-то показателей полученных расчетным путём должна быть равна какой-то фиксированной сумме, возможно придется где-то накидывать/убирать копейку, это распространенная проблема, но со способом представления чисел она тоже не связана.
Почему бы тогда уж fixed point numbers не юзать?
Re[7]: Числа с плавающей точкой
От: pagid Россия  
Дата: 07.04.16 09:29
Оценка:
Здравствуйте, FrozenHeart, Вы писали:

FH>Почему бы тогда уж fixed point numbers не юзать?

С фреймворков же обмениваешься в double, никакого смысла переводить из одного в другое и обратно нет. А так да, если бы программа не завесила от фреймворка, вариант с fixed point numbers возможно и был бы разумным. Но в любом же случае от проблем с округлением он не спасает. И в финансовых расчетах ситуации наподобие 1/3 сплошь и рядом, и результат все равно будет зависить от того сколько знаков после точки зафиксировать в "fixed point numbers" и когда округлять.
... << RSDN@Home 1.2.0 alpha 5 rev. 1495>>
Re[5]: Числа с плавающей точкой
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 07.04.16 09:39
Оценка:
Здравствуйте, FrozenHeart, Вы писали:

N>>Нет. Только конечные десятичные дроби. Ваш кэп.

FH>Но ведь неконечные хоть в каком виде представь -- они всё равно не могут быть отражены точно (тот же случай с 1/3).

Они могут быть отражены как 1/3 Но в общем да, вопрос не в этом. Вопрос в следующем. Представь себе, что у тебя десятичная плавучка с 6 цифрами мантиccы. Если ты вычисляешь 9999.99+0.01, получаешь 10000.0, и этот результат точный. Если 9999.99+0.02, получаешь 10000.0, и это уже результат неточный (7-я цифра не влезла). IEEE754 предоставляет возможность узнавать про такие неточные результаты через inexact condition (флаг или исключение), и если ты считаешь в десятичной, эта ошибка будет генерироваться только если действительно происходит потеря точности в результате выхода нужных тебе цифр за пределы представляемого (как в этом примере потеряли один цент), а если в двоичном, она будет происходить практически при любых вычислениях с дробными частями, и ты не опознаешь реальный факт такой потери точности на фоне такого шума.
Вот это то единственное, ради чего decimal float вообще могут быть нужны (а если инфраструктура такого не предоставляет, то она в целом бессмысленна, и лучше работать в более распространённых двоичных числах, или в fixed).
The God is real, unless declared integer.
floating point inexact exception
Re[8]: Числа с плавающей точкой
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 07.04.16 09:40
Оценка:
Здравствуйте, pagid, Вы писали:

P>Здравствуйте, FrozenHeart, Вы писали:


FH>>Почему бы тогда уж fixed point numbers не юзать?

P>С фреймворков же обмениваешься в double, никакого смысла переводить из одного в другое и обратно нет. А так да, если бы программа не завесила от фреймворка, вариант с fixed point numbers возможно и был бы разумным. Но в любом же случае от проблем с округлением он не спасает. И в финансовых расчетах ситуации наподобие 1/3 сплошь и рядом, и результат все равно будет зависить от того сколько знаков после точки зафиксировать в "fixed point numbers" и когда округлять.

Но в fixed это (как разделить на 3 и т.п.) контролируется явно, чем и лучше.
The God is real, unless declared integer.
Re[9]: Числа с плавающей точкой
От: pagid Россия  
Дата: 07.04.16 09:59
Оценка:
Здравствуйте, netch80, Вы писали:

N>Но в fixed это (как разделить на 3 и т.п.) контролируется явно, чем и лучше.

Как оно контролируется?
... << RSDN@Home 1.2.0 alpha 5 rev. 1495>>
Re[10]: Числа с плавающей точкой
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 07.04.16 10:15
Оценка:
Здравствуйте, pagid, Вы писали:

P>Здравствуйте, netch80, Вы писали:


N>>Но в fixed это (как разделить на 3 и т.п.) контролируется явно, чем и лучше.

P>Как оно контролируется?

Вручную если считается в целых типа 1/100 копейки, то в этих целых и поддерживается (например, чтобы 100 разрезалось на 33+33+34).
The God is real, unless declared integer.
Re[11]: Числа с плавающей точкой
От: pagid Россия  
Дата: 07.04.16 10:38
Оценка:
Здравствуйте, netch80, Вы писали:

N>Вручную если считается в целых типа 1/100 копейки,

Какой смысл считать в 1/100 копейки, а не в копейках?

N> то в этих целых и поддерживается (например, чтобы 100 разрезалось на 33+33+34).

Верно, уже упоминал об этом выше. Но тоже самое придется делать и при представлении чисел другими способами.

Но, например, нужно расчать что-то с участием в выражении не только денег, а каких-нибудь процентов или коээфициентов, и точность их представления нужна не 0.01 или даже не 0.0001. И получится выражение с разными типами чисел, их приведением возможно в одном выражение и вероятными проблемами. С double этой проблемы не будет.
Вовсе не призываю использовать double в финансовых расчетах, боже упаси Просто к тому, что отказ от "неточного" представления чисел вовсе не решает всех проблем и не отменяет необходимости думать.
... << RSDN@Home 1.2.0 alpha 5 rev. 1495>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.