Блин, сложная же тема эти числа с плавающей точкой. По крайней мере, для меня.
Вот есть 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 для передачи фреймворку?
— ???
Любые комментарии и советы приветствуются, буду признателен за ваши ответы.
Здравствуйте, 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 использовать. И не забывать, что даже поставив огромную длину и точности для чисел, всё равно можно придумать последовательность действий, для которых её не будет достаточно.
Здравствуйте, 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 очень неплохой вариант, или лучший, или единственно правильный. Но это как раз от назначения программы и характера расчетов зависит.
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 == 2for (int i = 0; i < 10000000; ++i)
{
int_numbernumber += static_cast<int>(0.1 * 100);
}
P>Преимущество тоже описал — "позволяют избегать ошибок округления, связанных с конвертацией между десятичным и двоичным представлением". Но про недостаток — меньшая скорость обработки, забыл.
Вы точно про binary floating point numbers говорите? Может, имелись ввиду decimal'ы? Цитата вроде неправильная.
P>Без знания области применения сложно что-то посоветовать.
Область применения -- финансовая сфера (да, и при этом фреймворк подсовывает double для хранения цен).
Операции могут быть самими разнообразными, ответил рядом -- грубо говоря, программа выполняет расчёты согласно формулам, занесённым пользователем в конфигурационный файл. Т.е. это может быть и сложение, и вычитание, и умножение, и деление.
P>Но если работа с тем фреймворком составляет существенную часть логики программы, вероятно использование double очень неплохой вариант, или лучший, или единственно правильный.
Да, операции над ценами выполняются довольно часто.
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 разве могут когда-то зафейлиться?
Здравствуйте, 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 и получить преимущество (?)
Если это финансы, да, так лучше. Иначе — см. выше.
Здравствуйте, FrozenHeart, Вы писали:
W>>Конечно можно. Ровно с таким же смыслом, как и обычные двоичные floating point числа FH>Я думал, что из-за того, что здесь любое "finite" число представляется однозначно, без "искажений", это вполне возможно.
Точно также 1/3 в десятичке не будет точным. И, как уже сказал, дребезг младшего разряда и погрешность округления растёт с ростом base.
FH>Однако в случае decimal'ов все "finite" числа представляются точно, без "искажений" и округлений, разве нет?
Здравствуйте, FrozenHeart, Вы писали:
FH>Вы точно про binary floating point numbers говорите? Может, имелись ввиду decimal'ы? Цитата вроде неправильная.
Сравниваю binary floating point numbers и decimal floating point numbers. И не вижу между ними особой разницы. Но нюансы есть.
FH>Область применения -- финансовая сфера (да, и при этом фреймворк подсовывает double для хранения цен).
Фреймворк изначально предназначен для финасовых расчетов?
FH>Операции могут быть самими разнообразными, ответил рядом -- грубо говоря, программа выполняет расчёты согласно формулам, занесённым пользователем в конфигурационный файл. Т.е. это может быть и сложение, и вычитание, и умножение, и деление.
Но только финасовые?
В фреймворк что передается, эти формулы и числовые данные, а возвращается результат расчета? Что с ним нужно сделать — показать пользователю, записать в БД или отправить в следущий по цепочке расчет.
P>>Но если работа с тем фреймворком составляет существенную часть логики программы, вероятно использование double очень неплохой вариант, или лучший, или единственно правильный. FH>Да, операции над ценами выполняются довольно часто.
Почему имеено над ценами?
Результат расчетов это денежные суммы с точностью до целых копеек(центов) или что-то другое?
N>Нет. Только конечные десятичные дроби. Ваш кэп.
Но ведь неконечные хоть в каком виде представь -- они всё равно не могут быть отражены точно (тот же случай с 1/3).
FH>>Область применения -- финансовая сфера (да, и при этом фреймворк подсовывает double для хранения цен). P>Фреймворк изначально предназначен для финасовых расчетов?
Фреймворк передаёт в мою программу информацию о проведённых операциях. Помимо всей остальной информации, там имеются и цены. Мне необходимо провести над ними некоторые манипуляции в виде арифметических операций и отдать обратно фреймворку, который уже засунет результат в кучу разных мест -- свою внутреннюю БД, в терминал клиентов и т.д.
P>Результат расчетов это денежные суммы с точностью до целых копеек(центов)
Да.
Здравствуйте, FrozenHeart, Вы писали:
FH>Фреймворк передаёт в мою программу информацию о проведённых операциях. Помимо всей остальной информации, там имеются и цены. Мне необходимо провести над ними некоторые манипуляции в виде арифметических операций и отдать обратно фреймворку, который уже засунет результат в кучу разных мест -- свою внутреннюю БД, в терминал клиентов и т.д.
Работай с double.
Если твои вычисления настолько сложны и многоэтапны, что могут воникнуть погрешности связанные с вычислениями, а не представлением чисел, можно делать округление перед возвратом значений в фреймворк. Возможно округление нужно и для каких-то промежуточных результатов, но это зависит от задачи. Обрати внимание на ситуации, когда сумма каких-то показателей полученных расчетным путём должна быть равна какой-то фиксированной сумме, возможно придется где-то накидывать/убирать копейку, это распространенная проблема, но со способом представления чисел она тоже не связана.
P>Работай с double. P>Если твои вычисления настолько сложны и многоэтапны, что могут воникнуть погрешности связанные с вычислениями, а не представлением чисел, можно делать округление перед возвратом значений в фреймворк. Возможно округление нужно и для каких-то промежуточных результатов, но это зависит от задачи. Обрати внимание на ситуации, когда сумма каких-то показателей полученных расчетным путём должна быть равна какой-то фиксированной сумме, возможно придется где-то накидывать/убирать копейку, это распространенная проблема, но со способом представления чисел она тоже не связана.
Почему бы тогда уж fixed point numbers не юзать?
Здравствуйте, FrozenHeart, Вы писали:
FH>Почему бы тогда уж fixed point numbers не юзать?
С фреймворков же обмениваешься в double, никакого смысла переводить из одного в другое и обратно нет. А так да, если бы программа не завесила от фреймворка, вариант с fixed point numbers возможно и был бы разумным. Но в любом же случае от проблем с округлением он не спасает. И в финансовых расчетах ситуации наподобие 1/3 сплошь и рядом, и результат все равно будет зависить от того сколько знаков после точки зафиксировать в "fixed point numbers" и когда округлять.
Здравствуйте, 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).
Здравствуйте, pagid, Вы писали:
P>Здравствуйте, FrozenHeart, Вы писали:
FH>>Почему бы тогда уж fixed point numbers не юзать? P>С фреймворков же обмениваешься в double, никакого смысла переводить из одного в другое и обратно нет. А так да, если бы программа не завесила от фреймворка, вариант с fixed point numbers возможно и был бы разумным. Но в любом же случае от проблем с округлением он не спасает. И в финансовых расчетах ситуации наподобие 1/3 сплошь и рядом, и результат все равно будет зависить от того сколько знаков после точки зафиксировать в "fixed point numbers" и когда округлять.
Но в fixed это (как разделить на 3 и т.п.) контролируется явно, чем и лучше.
Здравствуйте, pagid, Вы писали:
P>Здравствуйте, netch80, Вы писали:
N>>Но в fixed это (как разделить на 3 и т.п.) контролируется явно, чем и лучше. P>Как оно контролируется?
Вручную если считается в целых типа 1/100 копейки, то в этих целых и поддерживается (например, чтобы 100 разрезалось на 33+33+34).
Здравствуйте, netch80, Вы писали:
N>Вручную если считается в целых типа 1/100 копейки,
Какой смысл считать в 1/100 копейки, а не в копейках?
N> то в этих целых и поддерживается (например, чтобы 100 разрезалось на 33+33+34).
Верно, уже упоминал об этом выше. Но тоже самое придется делать и при представлении чисел другими способами.
Но, например, нужно расчать что-то с участием в выражении не только денег, а каких-нибудь процентов или коээфициентов, и точность их представления нужна не 0.01 или даже не 0.0001. И получится выражение с разными типами чисел, их приведением возможно в одном выражение и вероятными проблемами. С double этой проблемы не будет.
Вовсе не призываю использовать double в финансовых расчетах, боже упаси Просто к тому, что отказ от "неточного" представления чисел вовсе не решает всех проблем и не отменяет необходимости думать.
N>В общем случае — да, остаться на привычных binary floats. N>Причина очень проста: base=10 ничего положительного тебе не даст (у тебя уже на входе и выходе binary)
Ну да, на входе и на выходе у меня binary floats, но ведь decimal floats дадут мне возможность не беспокоиться о неточном представлении чисел наподобие 0.1 и спокойно складывать их, ожидая, что 0.1 + 0.1 + 0.1 на самом деле будет равно 0.3
Или Вы о том, что decimal я в итоге сконструирую на основе binary float, который уже содержит эту ошибку, в связи с чем перевод из binary в decimal уже мог дать "ошибочное" значение?
N>и они хуже потому, что с ростом base увеличиваются ошибки округления и дребезг значения младшего разряда.
С ростом base от 2 до 10?
N>Они могут быть отражены как 1/3 Но в общем да, вопрос не в этом. Вопрос в следующем. Представь себе, что у тебя десятичная плавучка с 6 цифрами мантиccы. Если ты вычисляешь 9999.99+0.01, получаешь 10000.0, и этот результат точный. Если 9999.99+0.02, получаешь 10000.0, и это уже результат неточный (7-я цифра не влезла). IEEE754 предоставляет возможность узнавать про такие неточные результаты через inexact condition (флаг или исключение), и если ты считаешь в десятичной, эта ошибка будет генерироваться только если действительно происходит потеря точности в результате выхода нужных тебе цифр за пределы представляемого (как в этом примере потеряли один цент), а если в двоичном, она будет происходить практически при любых вычислениях с дробными частями, и ты не опознаешь реальный факт такой потери точности на фоне такого шума. N>Вот это то единственное, ради чего decimal float вообще могут быть нужны (а если инфраструктура такого не предоставляет, то она в целом бессмысленна, и лучше работать в более распространённых двоичных числах, или в fixed).
Правильно ли я Вас понял, что Вы пытаетесь донести до меня мысль о том, что decimal floats точно так же подвержены накапливаемым ошибкам, как и binary floats? Только если в случае binary floats такое сплошь и рядом (из-за того, что "неточно" представляемых чисел в нём гораздо больше -- всякие 0.1 и т.д.), то в случае decimal floats такое будет происходить хотя бы из-за превышения точности. Верно?
Извините, что переспрашиваю, тема для меня кажется сложной, и я хочу убедиться, что правильно понимаю, о чём, собственно, идёт речь.
P>С фреймворков же обмениваешься в double, никакого смысла переводить из одного в другое и обратно нет. А так да, если бы программа не завесила от фреймворка, вариант с fixed point numbers возможно и был бы разумным. Но в любом же случае от проблем с округлением он не спасает. И в финансовых расчетах ситуации наподобие 1/3 сплошь и рядом, и результат все равно будет зависить от того сколько знаков после точки зафиксировать в "fixed point numbers" и когда округлять.
А есть ли у decimal floats уникальные для данного представления "неточные" значения наподобие того же 0.1 в случае binary floats?
Здравствуйте, FrozenHeart, Вы писали:
FH>А есть ли у decimal floats уникальные для данного представления "неточные" значения наподобие того же 0.1 в случае binary floats?
Наподобие 0.1 нет. Они будут получаться в результате расчетов.
P>Наподобие 0.1 нет.
А в случае binary floats они есть из-за того, что мы пытаемся провести конвертацию из десятичного представления в двоичный, верно? Т.е. к base == 2
P>Они будут получаться в результате расчетов
В двух случаях, да?
— При попытке представить конечным кол-вом знаков числа наподобие 1/3 (как это правильно назвать-то)
— При превышении точности
Здравствуйте, FrozenHeart, Вы писали:
FH>Правильно ли я Вас понял, что Вы пытаетесь донести до меня мысль о том, что decimal floats точно так же подвержены накапливаемым ошибкам, как и binary floats? Только если в случае binary floats такое сплошь и рядом (из-за того, что "неточно" представляемых чисел в нём гораздо больше -- всякие 0.1 и т.д.), то в случае decimal floats такое будет происходить хотя бы из-за превышения точности. Верно?
Ошибки связанные с "неточным" представлением в double некоторых десятичных чисел накапливаться не должны. Нужно ли для этого что-то делать в программе зависит от характера расчетов.
Здравствуйте, FrozenHeart, Вы писали:
FH>А в случае binary floats они есть из-за того, что мы пытаемся провести конвертацию из десятичного представления в двоичный, верно? Т.е. к base == 2
Да. Но в твоем случае они уже есть, "приходят" из упомянутого фреймворка
FH>- При попытке представить конечным кол-вом знаков числа наподобие 1/3 (как это правильно назвать-то)
Да
FH>- При превышении точности
При финансовых расчетов никакого превышения точности происходить не должно. Если предполагаешь, что оно при твоих расчетах он возможно, то нужно что-то делать. Для начала удостовериться, что эти опасения не на пустом месте. Затем, если это действительно так, в чем сомневаюсь, нужно увеличивать разрядность используемых представлений чисел.
P>Ошибки связанные с "неточным" представлением в double некоторых десятичных чисел накапливаться не должны
В смысле?
Вот есть, например, всё же то число 0.1, для которого binary float выбирает ближайшее представимое значение (какое-нибудь 0.10000000000000001). Если мы сложим такое число три раза, то явно получим не чистое 0.3, а что-то вроде 0.30000000000000003
FH>>А в случае binary floats они есть из-за того, что мы пытаемся провести конвертацию из десятичного представления в двоичный, верно? Т.е. к base == 2 P>Да. Но в твоем случае они уже есть, "приходят" из упомянутого фреймворка
Это да.
FH>>- При попытке представить конечным кол-вом знаков числа наподобие 1/3 (как это правильно назвать-то) P>Да
А каким термином это правильно называть, кстати, не подскажете?
FH>>- При превышении точности P>При финансовых расчетов никакого превышения точности происходить не должно.
Почему же? Представим чисто гипотетическую ситуацию.
Имеем огромный набор чисел, каждое из которых представляется при помощи binary floats с некоторой погрешностью. Если сложить их все вместе (например, для подсчёта приходов-расходов за определённый промежуток времени), вполне может накопиться такая ошибка, которая попадёт в значащие для меня разряды (например, пять знаков после запятой).
Здравствуйте, FrozenHeart, Вы писали:
FH>>>- При превышении точности P>>При финансовых расчетов никакого превышения точности происходить не должно. FH>Почему же? Представим чисто гипотетическую ситуацию. FH>Имеем огромный набор чисел, каждое из которых представляется при помощи binary floats с некоторой погрешностью. Если сложить их все вместе (например, для подсчёта приходов-расходов за определённый промежуток времени), вполне может накопиться такая ошибка, которая попадёт в значащие для меня разряды (например, пять знаков после запятой).
"Чисто гипотетически" сумма огромного набора чисел это не совсем типично для финансовых расчётов.
Если речь именно про суммирование, в значительном числе случаев помогает Kahan summation. Аналогичное можно придумать для умножения и деления, хотя и дорого
Но именно из-за таких опасений и переходят или на decimal float с контролем inexact condition, как я описывал раньше, или на fixed point.
Здравствуйте, FrozenHeart, Вы писали:
FH>А каким термином это правильно называть, кстати, не подскажете?
Не знаю. Из математики термины вспоминаются, а из IT нет
FH>Имеем огромный набор чисел, каждое из которых представляется при помощи binary floats с некоторой погрешностью.
В финансовых расчетах не бывает быть огромных наборов чисел.
FH> Если сложить их все вместе (например, для подсчёта приходов-расходов за определённый промежуток времени), вполне может накопиться такая ошибка,
Это очень небольшой набор чисел и ошибка накопится не может.
FH> которая попадёт в значащие для меня разряды (например, пять знаков после запятой).
Почему тебя интересует пять знаков после запятой?
Разумеется есть шанс нарваться на неприятности сложив тысяч 10 чисел имеющих порядок миллиардов и при этом представленных с точностью до копейки, но это странные финансовые расчеты, таких в жизни не бывает.
N>"Чисто гипотетически" сумма огромного набора чисел это не совсем типично для финансовых расчётов. N>Если речь именно про суммирование, в значительном числе случаев помогает Kahan summation. Аналогичное можно придумать для умножения и деления, хотя и дорого N>Но именно из-за таких опасений и переходят или на decimal float с контролем inexact condition, как я описывал раньше, или на fixed point.
Так, всё потихоньку встаёт на свои места, спасибо
Т.е. binary floats:
— Не могут точно представить некоторое множество дробных чисел и, как результат, могут накапливать ещё большую ошибку в результате выполнения над ними различных арифметических операций
Decimal floats:
— В отличие от binary floats, могут точно представить любую десятичную дробь
— Не могут представить определённое множество значений, получающихся в процессе выполнения арифметических операций (например, 1/3)
— Могут сигнализировать о выходе за пределы precision'а (inexact condition) при помощи специального флага или исключения
Fixed point numbers:
— Могут точно представить любое значение в пределах заданного precision'а, однако, очевидно, выполняют округление до заданного кол-ва знаков после запятой
Всё сказал правильно или всё же упустил что-то важное?
Здравствуйте, FrozenHeart, Вы писали:
FH>Правильно ли я Вас понял, что Вы пытаетесь донести до меня мысль о том, что decimal floats точно так же подвержены накапливаемым ошибкам, как и binary floats?
Не "точно так же", а даже больше. Если речь не идёт о числах, которые точно представимы в коротком виде. Например, decfloat64 от IEEE имеет ровно 16 десятичных цифр, и если считать до сотых долей цопеек, не будет проблем с операциями сложения, вычитания, умножения на целое, с любыми суммами менее триллиона — только за счёт точного представления.
Но моя мысль была чуть другая. Что если взять decfloat, включить исключение на inexact и считать что угодно, можно гарантировать, что проблемы будут опознаваться. А на операции, которые гарантированно неточны при стандартном расчёте (например, взять 1/7 часть от одного тугрика) — временно гасить генерацию исключения и не забывать чистить флаги после этого. С двоичным представлением и десятичными числами так не получится, будут постоянные ложные срабатывания. И это вторая важная причина использования именно десятичной плавучки (вплоть до аппаратной реализации, как в IBM zSeries).
FH> Только если в случае binary floats такое сплошь и рядом (из-за того, что "неточно" представляемых чисел в нём гораздо больше -- всякие 0.1 и т.д.), то в случае decimal floats такое будет происходить хотя бы из-за превышения точности. Верно?
При числах типа короткие десятичные дроби (типично для финансов) — именно так.
FH>Извините, что переспрашиваю, тема для меня кажется сложной, и я хочу убедиться, что правильно понимаю, о чём, собственно, идёт речь.
Да, inexact вообще такая штука, про которую крайне мало кто даже слышал.
Хотя, например, в Java и C# управлять им нельзя — там управление FP environment, включая условия ошибок, режимы округления и т.п., выкинули за непониманием (свалив проблемы на отдельные классы вроде BigFloat). Так что я не удивляюсь.
Здравствуйте, FrozenHeart, Вы писали:
N>>В общем случае — да, остаться на привычных binary floats. N>>Причина очень проста: base=10 ничего положительного тебе не даст (у тебя уже на входе и выходе binary) FH>Ну да, на входе и на выходе у меня binary floats, но ведь decimal floats дадут мне возможность не беспокоиться о неточном представлении чисел наподобие 0.1 и спокойно складывать их, ожидая, что 0.1 + 0.1 + 0.1 на самом деле будет равно 0.3
Это только при условии, что преобразование в этот самый decimal будет убирать уже накопленные ошибки округления.
FH>Или Вы о том, что decimal я в итоге сконструирую на основе binary float, который уже содержит эту ошибку, в связи с чем перевод из binary в decimal уже мог дать "ошибочное" значение?
Именно.
Если твёрдо известно, что вот эти числа надо округлить до 1/10000 — нет проблем. Если нет — если на входе было ~0.3000003, оно так и будет сохранено как 0.3000003 в десятичном и так и будет передаваться по цепочке расчёта.
N>>и они хуже потому, что с ростом base увеличиваются ошибки округления и дребезг значения младшего разряда. FH>С ростом base от 2 до 10?
Да.
В S/360 вообще было base16, и по ним эта проблема стуканула в полный рост (народ после IBM 704, 709, переходя на эти машины, матерился во много этажей). К тому же они в double умудрились выкинуть guard digits из промежуточных расчётов, и их так заклевали, что они в 68-м "отзывали" все машины на переделку за свой счёт. С тех пор народ знает, чем проблемны такие основания.
N>Но моя мысль была чуть другая. Что если взять decfloat, включить исключение на inexact и считать что угодно, можно гарантировать, что проблемы будут опознаваться. А на операции, которые гарантированно неточны при стандартном расчёте (например, взять 1/7 часть от одного тугрика) — временно гасить генерацию исключения и не забывать чистить флаги после этого. С двоичным представлением и десятичными числами так не получится, будут постоянные ложные срабатывания. И это вторая важная причина использования именно десятичной плавучки (вплоть до аппаратной реализации, как в IBM zSeries).
А, я-то думал, что inexact можно только для decimal floats выставлять. Вы же о функциях наподобие _controlfp_s говорите?
N>вплоть до аппаратной реализации, как в IBM zSeries
Т.е. как это? Что там аппаратно выполняется?
N>Хотя, например, в Java и C# управлять им нельзя — там управление FP environment, включая условия ошибок, режимы округления и т.п., выкинули за непониманием (свалив проблемы на отдельные классы вроде BigFloat). Так что я не удивляюсь.
Не, я в основном на C++ пишу, но вопрос общий, это да.
N>>>и они хуже потому, что с ростом base увеличиваются ошибки округления и дребезг значения младшего разряда. FH>>С ростом base от 2 до 10?
Не совсем понимаю, почему ошибки округления увеличиваются. Можно пример или какое-нибудь пояснение, пожалуйста?
Здравствуйте, FrozenHeart, Вы писали:
FH>А, я-то думал, что inexact можно только для decimal floats выставлять.
Нет.
FH> Вы же о функциях наподобие _controlfp_s говорите?
Да.
N>>вплоть до аппаратной реализации, как в IBM zSeries FH>Т.е. как это? Что там аппаратно выполняется?
Именно плавучка по основанию 10 согласно IEEE 754-2008. Основные операции с ней.
Причём представление мантиссы не двоичное, как в GCC эмуляции libdecimal, а 3-to-10 (Chen-Ho encoding), что "доставляет" тем, кто этого не видел раньше Это они продавили такое чудо в стандарт
Здравствуйте, pagid, Вы писали:
P>Здравствуйте, netch80, Вы писали:
N>>Вручную если считается в целых типа 1/100 копейки, P>Какой смысл считать в 1/100 копейки, а не в копейках?
Это не ко мне вопрос. Ряд стандартов так требуют для промежуточных значений в расчётах (на самом деле речь шла про США и центы).
Вероятно, этим как раз и решают проблемы округления промежуточных значений — не копал эту тему.
N>> то в этих целых и поддерживается (например, чтобы 100 разрезалось на 33+33+34). P>Верно, уже упоминал об этом выше. Но тоже самое придется делать и при представлении чисел другими способами.
Я говорю именно о задаче. В матфизике нет необходимости разделять число на части с точным совпадением суммы этих частей до последней значащей цифры. В финансах это обязательное условие при любой подобной операции — "итого", остатки и балансы должны сходиться точно, где вообще компьютер может этому помочь.
P>Но, например, нужно расчать что-то с участием в выражении не только денег, а каких-нибудь процентов или коээфициентов, и точность их представления нужна не 0.01 или даже не 0.0001. И получится выражение с разными типами чисел, их приведением возможно в одном выражение и вероятными проблемами. С double этой проблемы не будет. P>Вовсе не призываю использовать double в финансовых расчетах, боже упаси Просто к тому, что отказ от "неточного" представления чисел вовсе не решает всех проблем и не отменяет необходимости думать.
Верно, как и переход на флоаты не убивает эту возможность. Но усиливает необходимость думать.
Здравствуйте, netch80, Вы писали:
N>Это не ко мне вопрос. Ряд стандартов так требуют для промежуточных значений в расчётах (на самом деле речь шла про США и центы).
Вот именно промежуточных, по нашим правилам. За США не скажу.
N>Я говорю именно о задаче. В матфизике нет необходимости разделять число на части с точным совпадением суммы этих частей до последней значащей цифры. В финансах это обязательное условие при любой подобной операции — "итого", остатки и балансы должны сходиться точно, где вообще компьютер может этому помочь.
+1
Здравствуйте, FrozenHeart, Вы писали:
FH>Вот есть, например, всё же то число 0.1, для которого binary float выбирает ближайшее представимое значение (какое-нибудь 0.10000000000000001). Если мы сложим такое число три раза, то явно получим не чистое 0.3, а что-то вроде 0,30000000000000003
Прикинуть можно, зная количество значащих цифр в том же double.
Свою оценку уже приводил — сложение многих тысяч миллиардных сумм представленных с точностью до копейки, но это близко к той границе, где точности double может не хватить в принципе для представления точных сумм, а не только потому, что они в двоичном виде представляются не точно.
И на финансовые расчеты это не похоже.
Здравствуйте, FrozenHeart, Вы писали:
N>>>>и они хуже потому, что с ростом base увеличиваются ошибки округления и дребезг значения младшего разряда. FH>>>С ростом base от 2 до 10? FH>Не совсем понимаю, почему ошибки округления увеличиваются. Можно пример или какое-нибудь пояснение, пожалуйста?
Представим себе случаи с очень близкой точностью — например, 6 десятичных разрядов (точность до 1/1000000 у самоого большого значения мантиссы) или 20 двоичных (до 1/1048576 соответственно).
Число от 524288 до 999999 представляется в обоих случаях с точностью до 1.
От 1000000 до 1048576 — в двоичном до 1, в десятичном до 10.
Вниз:
От 262144 до 524288 — в двоичном до 1/2 (различаются, например, последовательно 345678, 345678.5 и 345679), но в десятичном — до 1.
От 131072 до 262144 — в двоичном до 1/4, в десятичном до 1.
От 100000 до 131072 — в двоичном до 1/8, в десятичном до 1.
От 65536 до 100000 — в двоичном до 1/8, в десятичном до 1/10 (да, небольшой период наоборот).
От 32768 до 65536 — в двоичном до 1/16, в десятичном до 1/10.
И так далее.
В случае base16 (S/360) случаев, когда десятичная точнее, вообще нет.
Второй фактор — резкое возрастание неточности округления младшего разряда, сама по себе ситуация, когда два очень близких значения округляются до границы, которая для них разная не в 2, а в 10 или 16 раз. Тут закономерности сложнее, без формул не обойтись. Некоторое обсуждение есть в FMM.
Здравствуйте, FrozenHeart, Вы писали:
N>>"Чисто гипотетически" сумма огромного набора чисел это не совсем типично для финансовых расчётов. N>>Если речь именно про суммирование, в значительном числе случаев помогает Kahan summation. Аналогичное можно придумать для умножения и деления, хотя и дорого N>>Но именно из-за таких опасений и переходят или на decimal float с контролем inexact condition, как я описывал раньше, или на fixed point. FH>Так, всё потихоньку встаёт на свои места, спасибо
FH>Т.е. binary floats: FH>- Не могут точно представить некоторое множество дробных чисел и, как результат, могут накапливать ещё большую ошибку в результате выполнения над ними различных арифметических операций
Это одинаково для всех. Отличаются они тут явно именно для конечных десятичных дробей.
FH>Decimal floats: FH>- В отличие от binary floats, могут точно представить любую десятичную дробь
_Конечную_ и ограниченной длины. А то число π — это тоже десятичная дробь
FH>- Не могут представить определённое множество значений, получающихся в процессе выполнения арифметических операций (например, 1/3)
Одинаково для обоих.
FH>- Могут сигнализировать о выходе за пределы precision'а (inexact condition) при помощи специального флага или исключения
Одинаково для обоих.
FH>Fixed point numbers: FH>- Могут точно представить любое значение в пределах заданного precision'а, однако, очевидно, выполняют округление до заданного кол-ва знаков после запятой FH>Всё сказал правильно или всё же упустил что-то важное?
N>Это одинаково для всех. Отличаются они тут явно именно для конечных десятичных дробей.
В общем, decimal floats не могут представить лишь неконечные дроби наподобие числа Pi?
N>_Конечную_ и ограниченной длины. А то число π — это тоже десятичная дробь
А в чём разница между конечной дробью и дробью с ограниченной длиной?
Здравствуйте, FrozenHeart, Вы писали: FH>- Продолжать использовать double'ы, не забывая о корректном сравнении и прочих подводных камнях
double вполне достаточно в большинстве случаев. а есть кстати, еще long double, стоит попробовать сравнить результаты с обеими типами
FH>- Начать использовать decimal floating point numbers и получить преимущество (?). Сравнивать их через operator== без epsilon можно, кстати?
это для бухгалтерии, так как даже с long double часто бывают числа в десятичной записи выглядящие как 1 + 2 = 2.99999999999999999999999999999999999
приходится применять округление даже там, где его никто не ждет. а у бухгалтерии еще и округление свое (ога) и это рождает большую путаницу. поэтому где деньги, там decimal, и где decimal — там деньги
FH>- Начать использовать fixed point numbers и получить преимущество (?)
это используется в геймдеве для девайсов без floating point. очень неудобный и стремный тип
FH>- Переводить double'ы в int'ы, выполнять над ними все необходимые операции и конвертировать обратно в double для передачи фреймворку?
это еще зачем?
Здравствуйте, FrozenHeart, Вы писали:
N>>_Конечную_ и ограниченной длины. А то число π — это тоже десятичная дробь FH>А в чём разница между конечной дробью и дробью с ограниченной длиной?
Имелось в виду — чтобы влезала в размер мантиссы данного типа.
Для IEEE'шных decfloat32 это 7 цифр, decfloat64 — 16.
__>double вполне достаточно в большинстве случаев. а есть кстати, еще long double, стоит попробовать сравнить результаты с обеими типами
long double в случае Visual C++ то же самое, что и double, а в стандарте C++ и вовсе сказано следующее:
There are three floating point types: float, double, and long double. The type double provides at least as much precision as float, and the type long double provides at least as much precision as double
__>это используется в геймдеве для девайсов без floating point. очень неудобный и стремный тип
А что неудобного и стрёмного?
__>это еще зачем?
Сложение, вычитание и умножение без погрешностей?
Если у вас вычислений относительно много (например в цикле идут умножения/деления/сложения/вычитания), то с рациональными числами их компоненты очень быстро станут гигантскими, а арифметика с длинными числами это тоже не подарок. Ну и куча операций типа корня и тд над рациональными числами тоже дадут неточный результат. Так что это не панацея. Лучше разберитесь с ошибками округлениями и возьмите их под контроль.
Здравствуйте, FrozenHeart, Вы писали:
N>>Имелось в виду — чтобы влезала в размер мантиссы данного типа. N>>Для IEEE'шных decfloat32 это 7 цифр, decfloat64 — 16. FH>Понял, спасибо.
FH>На второй вопрос можете ответить, пожалуйста?
>>В общем, decimal floats не могут представить лишь неконечные дроби наподобие числа Pi?
А что непонятно? Я считаю предыдущее ответом на оба вопроса. Вообще, должно быть очевидно, что если само понятие "конечная дробь" введено в контексте десятичной арифметики, то и десятичный флоат будет конечным, а если нет — то надо перемерять и уточнять
Здравствуйте, FrozenHeart, Вы писали: __>>double вполне достаточно в большинстве случаев. а есть кстати, еще long double, стоит попробовать сравнить результаты с обеими типами FH>long double в случае Visual C++ то же самое, что и double, а в стандарте C++ и вовсе сказано следующее:
в gcc есть (?) __float128 и в случае отсутсвия поддержки всегда можно сэмулировать, возможно, достаточно эффективно. по крайней мере точно эффективнее decimal
__>>это используется в геймдеве для девайсов без floating point. очень неудобный и стремный тип FH>А что неудобного и стрёмного?
обычно одновременно совмещает в себе низкую точность и быстрое переполнение
FH>Сложение, вычитание и умножение без погрешностей?
если у нас дробной части нет и значения маленькие, то да. а такое часто бывает?
Здравствуйте, FrozenHeart, Вы писали:
FH> Вот такие ситуации в случае decimal floating point numbers разве могут когда-то зафейлиться?
Такие вещи нужно обсуждать с бизнес-аналистом или domain expert.
Все правила округления при вычислениях и порядок вычислений диктуется только бизнесом.
И никакое представление чисел тебе не поможет.
В финансах люди обычно используют десятичные числа, поэтому и в компьютере вычисления должны быть десятичными, с учётом требуемых способов округления.
Например, такой сценарий. Налоговая декларация, 3 партнёра ведут бизнес и получили прибыль $1000. По уставу компании участие в бизнесе описывается как равная доля.
Соответственно каждый получил $1000/3, что никак не делится. Поэтому в налоговой декларации предусмотрен флажок, который должен быть поставлен одному из партнёров — кому идут остатки: $333+$334+$333.
Или стандартная грабля. Продаёшь свой товар за $99.99, и должен платить НДС 20%.
Если ты продал 1000 единиц товара, то сколько ты должен заплатить НДС?
налог с продажи тысячи единиц товара:
(1000 * $99.99) * 20% = $9999 * 20% = $19998
или
сумма тысяч налогов с продажи каждого товара:
1000 * ($99.99 * 20%) = 1000 * $19.99 = $19990
какой ответ правильный? А оба могут быть правильные — зависит от требований бизнеса. И, кстати, вариантов — больше двух, я указал лишь два простейших.
Здравствуйте, ·, Вы писали:
·>Такие вещи нужно обсуждать с бизнес-аналистом или domain expert.
А если ТС сам себе бизнес-аналитик и этот самый "domain expert". Не все же на конвейере стоят.
·>Все правила округления при вычислениях и порядок вычислений диктуется только бизнесом. ·>И никакое представление чисел тебе не поможет.
ТС интересуется не внесет ли представление чисел дополнительных проблем никак не связанных с бизнес-логикой.
·>В финансах люди обычно используют десятичные числа, поэтому и в компьютере вычисления должны быть десятичными, с учётом требуемых способов округления.
У ТС свои особенности, в его продукте задействован (и похоже не на последних ролях) некий "фреймворк" работающий с double.
И инженеры и ученые тоже используют десятичные числа, но это не значит, что при научных и инженерных расчетах компьютере вычисления должны быть десятичными.
·>Например, такой сценарий. Налоговая декларация, 3 партнёра ведут бизнес и получили прибыль $1000. По уставу компании участие в бизнесе описывается как равная доля. ·>Соответственно каждый получил $1000/3, что никак не делится. Поэтому в налоговой декларации предусмотрен флажок, который должен быть поставлен одному из партнёров — кому идут остатки: $333+$334+$333.
Да, "последняя копейка" должна распределятся, если сумма задана.
·>какой ответ правильный? А оба могут быть правильные — зависит от требований бизнеса.
Первый, если мы в России.
·>И, кстати, вариантов — больше двух, я указал лишь два простейших.
+1
... << RSDN@Home 1.2.0 alpha 5 rev. 1495>>
Re: предлагаю ознакомиться со статьей на соседней ветке
__>в gcc есть (?) __float128 и в случае отсутсвия поддержки всегда можно сэмулировать, возможно, достаточно эффективно. по крайней мере точно эффективнее decimal
Ясно, спасибо за информацию.
__>обычно одновременно совмещает в себе низкую точность и быстрое переполнение
Точность же точно такая, какую указал разработчик, так что разве это может быть минусом?
Быстро переполнение чего?
__>если у нас дробной части нет и значения маленькие, то да. а такое часто бывает?
Тут согласен -- фигня.
Здравствуйте, pagid, Вы писали:
P>Статья несколько "в сторону". В сторону ПЛ/1.
дело вовсе не в PL/1 (на самом деле не в Коболе?)
Дело в том, что эти вопросы давно решены, в том числе и на аппаратном уровне — двоично-десятичная арифметика.
И вставлены в процессоры. А теперь начинают велосипед изобретать. Кобол уже решил вопросы финансовых расчетов, почему его решения не взять в другие языки?
Здравствуйте, FrozenHeart, Вы писали:
FH>·>Такие вещи нужно обсуждать с бизнес-аналистом или domain expert. FH>К сожалению (?), у нас нет таких людей.
Тогда ты сам должен знать или договориться с клиентами о том, что будет правильным резульатом. Твой вопрос аналогичен "какую мне операцию использовать — сложение или умножение, какая считает точнее"? Тот факт эти операции иногда дают одинаковый ответ не означает что они заменяемы. Ты должен знать что именно ты считаешь и использовать соответствующую арифметику.
Все эти представления чисел не позволяют делать произвольные вычисления с абсолютной точностью.
Я наблюдал, например, что налоговка использует округления в пользу налогоплательщика — налогооблагаемая прибыль округляется вниз, а налоговые вычеты — вверх.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, pagid, Вы писали:
P>·>Такие вещи нужно обсуждать с бизнес-аналистом или domain expert. P>А если ТС сам себе бизнес-аналитик и этот самый "domain expert". Не все же на конвейере стоят.
Тогда он должен обсуждать с клиентами как они думают должно вычисляться чтобы они были довольны.
P>·>Все правила округления при вычислениях и порядок вычислений диктуется только бизнесом. P>·>И никакое представление чисел тебе не поможет. P>ТС интересуется не внесет ли представление чисел дополнительных проблем никак не связанных с бизнес-логикой.
Единственная такая проблема — перформанс, т.е. эффективность конкретных алгоритмов на конкретной платформе. Остальное — определяется бизнес-логикой.
Да и собственно перформанс тоже задаётся бизнес-логикой. Мы например, в критическом месте для финансов использовали double вместо BigDecimal — ибо в данном случае было важнее дать результат быстрее, а не точнее.
P>·>В финансах люди обычно используют десятичные числа, поэтому и в компьютере вычисления должны быть десятичными, с учётом требуемых способов округления. P>У ТС свои особенности, в его продукте задействован (и похоже не на последних ролях) некий "фреймворк" работающий с double. P>И инженеры и ученые тоже используют десятичные числа, но это не значит, что при научных и инженерных расчетах компьютере вычисления должны быть десятичными.
Не должны, но могут, это просто другая область. Например, строители могут считать в миллиметрах. Т.е. просто другая предметная область, правила вычислений те же.
P>·>какой ответ правильный? А оба могут быть правильные — зависит от требований бизнеса. P>Первый, если мы в России.
Да это не от страны зависит, вроде. Например, продажа по цене $99.99/кг, и продав 10кг сотне клиентов это не то же самое что продажа 100кг десяти. Т.е. налог может считаться не на единицу товара, а на транзакцию.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: предлагаю ознакомиться со статьей на соседней ветке
Здравствуйте, Privalov, Вы писали:
P>Потому что эти решения до сих пор используются на Коболе. Перетаскивать их куда-то еще просто не нужно.
Тогда почему возникают такие ветки?
Очередной программист узнал, что числа IEEE-754 представляются неточно?
И начинается очередное вычисление квадратуры круга.
А если бы механизм точных вычислений был бы просто перенесен в современные языки (как из Кобола в ПЛ/1), то и вопросов бы не было — для финансово-экономических расчетов применяй то-то и то-то.
Re[6]: предлагаю ознакомиться со статьей на соседней ветке
Здравствуйте, кт, Вы писали:
кт>Очередной программист узнал, что числа IEEE-754 представляются неточно?
Да. кт>И начинается очередное вычисление квадратуры круга.
Да. А на самом деле оно не нужно.
кт>А если бы механизм точных вычислений был бы просто перенесен
Не нравится мне это Ваше "механизм точных вычислений", очень уж напоминает смятение вышеупомянутого программиста только узнавшего...
кт>в современные языки (как из Кобола в ПЛ/1), то и вопросов бы не было — для финансово-экономических расчетов применяй то-то и то-то.
Почему именно Кобол и ПЛ/1, чем не устраивает например decimal из C# ?
Здравствуйте, pagid, Вы писали:
P>Вновь займетесь вычислением квадратуры круга?
Как раз я ей не занимаюсь и не пытаюсь использовать для финансовых расчетов мантиссу и порядок.
А пример с 1/3 непоказателен. В большинстве случаев в финансах нужны не рациональные дроби, а проценты (и особенно сложные проценты). Как раз они прекрасно представляются в десятичном виде. Автор старой статьи пытался донести для чего в IA-32 введена двочная арифметика, Float-арифметика и BCD-арифметика.
Двоичная — для "обычных" вычислений
float — для научных
двоично-десятичная — для финансовых.
Не считайте программистов 60-х и 70-х за дикарей с луком и стрелами.
P.S. в статье объяснялась, чем не устраивает Decimal С#
Re[4]: предлагаю ознакомиться со статьей на соседней ветке
Здравствуйте, кт, Вы писали:
P>>Статья несколько "в сторону". В сторону ПЛ/1.
кт>дело вовсе не в PL/1 (на самом деле не в Коболе?) кт>Дело в том, что эти вопросы давно решены, в том числе и на аппаратном уровне — двоично-десятичная арифметика. кт>И вставлены в процессоры.
Уже не вставлены. В x86-64 двоично-десятичные команды (AA? и DA?) отменены. Можно их эмулировать, но снова на чистой бинарке.
кт> А теперь начинают велосипед изобретать. Кобол уже решил вопросы финансовых расчетов, почему его решения не взять в другие языки?
Расскажите подробнее, пожалуйста, как он именно их решил. Тем более, что статья по ссылке говорила про PL/1, а не Cobol.
The God is real, unless declared integer.
Re[7]: предлагаю ознакомиться со статьей на соседней ветке
Здравствуйте, pagid, Вы писали:
кт>>в современные языки (как из Кобола в ПЛ/1), то и вопросов бы не было — для финансово-экономических расчетов применяй то-то и то-то. P>Почему именно Кобол и ПЛ/1, чем не устраивает например decimal из C# ?
Ну меня он не устраивает сразу по нескольким параметрам:
1. Никак не решаются вопросы фиксации особых ситуаций и/или перевод их в исключения.
Что будет при переполнении? Недополнении? Потере значащих цифр?
2. Вместо следования уже планировавшимся на момент его введения IEEE754 decimal floats, изобрели свой велосипед. Причём с кучей странных решений. Например, почему порядок только от 0 до 28?
The God is real, unless declared integer.
Re[5]: предлагаю ознакомиться со статьей на соседней ветке
Здравствуйте, netch80, Вы писали:
N>Уже не вставлены. В x86-64 двоично-десятичные команды (AA? и DA?) отменены. Можно их эмулировать, но снова на чистой бинарке.
Вновь пришедшие инженеры в Интел никогда не видели двоично-десятичных расчетов Они и операцию INTO выкинули (идиоты).
Про "чистую бинарку" не понял, вот, например, эмуляции DAA
;--------------- ЭМУЛЯЦИЯ DAA, ЗАПРЕЩЕННОЙ В РЕЖИМЕ X86-64 ------------
PUBLIC DAA_X86_64:
PUSH RDX,RAX
LAHF
MOV EDX,EAX ;OLD CF И OLD AL
AND AH,NOT 1B ;СБРОСИЛИ CF
;---- ОБРАБОТКА МЛАДШЕЙ ТЕТРАДЫ ----
TEST AH,10000B ;ЕСЛИ ЕСТЬ AF
JNZ @
PUSH RAX
AND AL,0FH
CMP AL,9 ;ИЛИ ЕСЛИ ЦИФРА БОЛЬШЕ 9
POP RAX
JBE M2270
@: ADD AL,6 ;КОРРЕКЦИЯ ЦИФРЫ
OR AH,10000B ;УСТАНАВЛИВАЕМ AF
;---- ОБРАБОТКА СТАРШЕЙ ТЕТРАДЫ ----
M2270:TEST DH,1B ;ЕСЛИ СТОЯЛ OLD CF
JNZ @
CMP DL,99H ;ИЛИ НУЖЕН ПЕРЕНОС
JBE M2271
@: OR AH,1B ;УСТАНАВЛИВАЕМ CF
ADD AL,60H ;КОРРЕКЦИЯ ТЕТРАДЫ
;---- ПИШЕМ ГОТОВЫЙ БАЙТ И ВОССТАНАВЛИВАЕМ РЕГИСТРЫ И ФЛАГИ ----
M2271:SAHF
MOV [ESP],AL
POP RAX,RDX
RET
кт>> А теперь начинают велосипед изобретать. Кобол уже решил вопросы финансовых расчетов, почему его решения не взять в другие языки?
N>Расскажите подробнее, пожалуйста, как он именно их решил. Тем более, что статья по ссылке говорила про PL/1, а не Cobol.
В PL/1 ничего нового не добавили, взяли из Кобола.
Представьте, что у вас нет компьютера, а есть ручка и бумага. Можно же сосчитать нужные проценты и т.п., умножая и складывая столбиком.
При этом числа точные, хотя и не обязательно целые. Именно это и было разработано для Кобола — точное представление в десятичном виде, практически в виде текста на бумаге.
Двоично-десятичные расчеты — аналог расчетов на бумаге. Это было реализовано 60 лет назад. И американские бухгалтеры не жаловались
Re[8]: предлагаю ознакомиться со статьей на соседней ветке
Здравствуйте, кт, Вы писали:
кт>Как раз я ей не занимаюсь и не пытаюсь использовать для финансовых расчетов мантиссу и порядок. кт>А пример с 1/3 непоказателен. В большинстве случаев в финансах нужны не рациональные дроби, а проценты (и особенно сложные проценты). Как раз они прекрасно представляются в десятичном виде. Автор старой статьи пытался донести для чего в IA-32 введена двочная арифметика, Float-арифметика и BCD-арифметика. кт>Двоичная — для "обычных" вычислений кт>float — для научных кт>двоично-десятичная — для финансовых.
Не смешите. Двоично-десятичная в 8080 (!) введена для работы с устройствами, которые вводят и выводят короткие числа в десятичном виде, как часы. Это был процессор для микроконтроллеров, и предложение использовать его для финансов встретило бы только дикое недоумение всех понимающих.
На этой "арифметике" вы нормально сделаете только сложение и вычитание — любых размеров, умножение и деление — только на одну цифру. Всё более сложное на ней выливается в дикий гемор, что проще делать через двоичку. Хорошо же средство для "финансов", что не может нормально вычислить несчастные 18% VAT или 3.3% сложного процента по кредиту.
В 8086 во всей линии вплоть до последних 32-разрядных версий это просто наследие, которое в линии IBM PC перестало быть ценным. В x86-64 его исключили как то, что уже лет 15 перед этим перестало быть хоть кому-то ценным.
кт>Не считайте программистов 60-х и 70-х за дикарей с луком и стрелами.
Это вы их такими, похоже, считаете, если выдвигаете идеи использовать x86 BCD для финансов. Они использовали S/360, S/370 и аналоги.
В этой линии есть, например, такие команды процессора, как конверсия из bin в dec и обратно, сложение, вычитание, умножение и деление, округление, которые работают над числами в памяти, например, AP — сложить два упакованных (2 цифры на байт) числа длиной до 16 байт (31 цифра и знак). Вот это — у программистов 60-70-х. А лук и стрелы в этой области начались позже, с массовым расползанием персоналок...
The God is real, unless declared integer.
Re[6]: предлагаю ознакомиться со статьей на соседней ветке
Здравствуйте, кт, Вы писали:
кт>Здравствуйте, netch80, Вы писали:
N>>Уже не вставлены. В x86-64 двоично-десятичные команды (AA? и DA?) отменены. Можно их эмулировать, но снова на чистой бинарке. кт>Вновь пришедшие инженеры в Интел никогда не видели двоично-десятичных расчетов Они и операцию INTO выкинули (идиоты).
1. Это были AMD, а не Intel.
2. С современными архитектурами никакого смысла в этих командах не стало. Не считайте их настолько идиотами. Команды типа DAA нужны только процессорам, которые не умеют быстрой арифметики, для работы, в которой от ввода числа в текстовом десятичном выводе до его вывода выполняется максимум десяток одноцифровых операций. Ни первое, ни второе не соответствуют современному железу и современным задачам.
кт>Про "чистую бинарку" не понял, вот, например, эмуляции DAA
А теперь померяйте время работы этого всего кошмара для 16 цифр и сравните со временем одного сложения в double.
кт>>> А теперь начинают велосипед изобретать. Кобол уже решил вопросы финансовых расчетов, почему его решения не взять в другие языки? N>>Расскажите подробнее, пожалуйста, как он именно их решил. Тем более, что статья по ссылке говорила про PL/1, а не Cobol. кт>В PL/1 ничего нового не добавили, взяли из Кобола.
Если речь про идею numeric с двумя фиксированными размерами, это раньше Кобола.
кт>Представьте, что у вас нет компьютера, а есть ручка и бумага. Можно же сосчитать нужные проценты и т.п., умножая и складывая столбиком. кт>При этом числа точные, хотя и не обязательно целые. Именно это и было разработано для Кобола — точное представление в десятичном виде, практически в виде текста на бумаге.
кт>Двоично-десятичные расчеты — аналог расчетов на бумаге. Это было реализовано 60 лет назад. И американские бухгалтеры не жаловались
Именно такие расчёты сейчас ведут в Fixed и не требуют никаких BCD в процессоре.
The God is real, unless declared integer.
Re[9]: предлагаю ознакомиться со статьей на соседней ветке
Здравствуйте, netch80, Вы писали:
N>Не смешите. Двоично-десятичная в 8080 (!) введена для работы с устройствами, которые вводят и выводят короткие числа в десятичном виде, как часы. Это был процессор для микроконтроллеров, и предложение использовать его для финансов встретило бы только дикое недоумение всех понимающих. N>На этой "арифметике" вы нормально сделаете только сложение и вычитание — любых размеров, умножение и деление — только на одну цифру. Всё более сложное на ней выливается в дикий гемор, что проще делать через двоичку. Хорошо же средство для "финансов", что не может нормально вычислить несчастные 18% VAT или 3.3% сложного процента по кредиту.
N>В 8086 во всей линии вплоть до последних 32-разрядных версий это просто наследие, которое в линии IBM PC перестало быть ценным. В x86-64 его исключили как то, что уже лет 15 перед этим перестало быть хоть кому-то ценным.
Боюсь, Вы не понимаете смысла BCD-арифметики. Она не для коротких чисел, а для чисел любой длины. Точно так же как операции ADC и SBB позволяют за счет учета переноса реализовать арифметику люблй длины, например, миллион знаков. И это можно было делать даже на 8080 (была версия PL/1 и для такого процессора) и ответ получается правильный
Re[10]: предлагаю ознакомиться со статьей на соседней ветке
Здравствуйте, кт, Вы писали:
кт>Боюсь, Вы не понимаете смысла BCD-арифметики. Она не для коротких чисел, а для чисел любой длины.
Это Вы не понимаете, что она нахрен не нужна для чисел "любой длины" в таком виде. Настолько неэффективна, что дешевле перевести в двоичку и всё подсчитать в ней, чем маяться дурью по одной десятичной цифре.
кт> Точно так же как операции ADC и SBB позволяют за счет учета переноса реализовать арифметику люблй длины, например, миллион знаков.
Там хотя бы по 32/64, а не по 4 (в лучшем случае 8, не для всех действий).
The God is real, unless declared integer.
Re[11]: предлагаю ознакомиться со статьей на соседней ветке
Здравствуйте, кт, Вы писали:
N>>Это Вы не понимаете, что она нахрен не нужна для чисел "любой длины" в таком виде. кт>На такие доводы у меня нет аргументов. Сдаюсь.
Теоретически аргументы у вас есть. Выпустить новую архитектуру, которая бы имела такие операции. Или убедить принять даже как специализированное расширение к существующей. Например, вон RISC-V сейчас очень активно пилят, можно присоединиться. У них ещё ничего окончательно не застыло, реализаций в ASIC нет, максимум что есть — трансляция в FPGA — легко переписывается. Попробуйте убедить
На практике же, я думаю, реальность такого результата — 0%.
В AArch64, например, куча неожиданных решений — типа того, что нет отдельной операции целочисленного умножения, а есть целочисленная FMA(!) и для умножения в качестве слагаемого задаётся null register. То есть видно, что авторы крепко перепланировали всю реализацию. Но intBCD у них нет Как нет и в любой архитектуре толще, чем 8-битный камень уровня "нам надо на чём-то будильник построить", разработанной хотя бы в 1970. (В S/370 и позже группа команд intBCD сохраняется только как наследие проклятого прошлого.)
Так что — пробуйте. Если получится, будет прикольно. Но — не верю
Здравствуйте, ·, Вы писали:
·>Я наблюдал, например, что налоговка использует округления в пользу налогоплательщика — налогооблагаемая прибыль округляется вниз, а налоговые вычеты — вверх.
А еще, например, авансовые платежи в налоговую округляются не до копеек, а до целых рублей.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Здравствуйте, Ops, Вы писали:
Ops>А еще, например, авансовые платежи в налоговую округляются не до копеек, а до целых рублей.
И все налоги уже давно округляются до рублей. При этом рекомендуется использовать обычное арифметическое округление.
Здравствуйте, pagid, Вы писали:
Ops>>А еще, например, авансовые платежи в налоговую округляются не до копеек, а до целых рублей. P>И все налоги уже давно округляются до рублей. При этом рекомендуется использовать обычное арифметическое округление.
Мнэээ... 14.50 округлять рекомендовано до 14 или 15?
А 15.50?
Здравствуйте, netch80, Вы писали:
N>Мнэээ... 14.50 округлять рекомендовано до 14 или 15? N>А 15.50?
Мне показалось, что в России в нормативных документах способ округления прямо не устанавливается. Но разумеется принято округлять 14.50 до 15, а 15.50 до 16. Если где-то программы округляют по "импортным" правилам вряд ли кого-то это волнует или вызывает какие-то недоразумения.
Здравствуйте, Ops, Вы писали:
Ops>·>Я наблюдал, например, что налоговка использует округления в пользу налогоплательщика — налогооблагаемая прибыль округляется вниз, а налоговые вычеты — вверх. Ops>А еще, например, авансовые платежи в налоговую округляются не до копеек, а до целых рублей.
Да, в спеках UK-налоговой там шесть видов округлений: до пенсов, до фунтов * вверх, вниз, банковское.
Вопрос — возможно ли реализовать эти округления используя тип double?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Тогда он должен обсуждать с клиентами как они думают должно вычисляться чтобы они были довольны.
ТС с нам обсуждал вопрос реализации не касающийся пользователей — какое внутреннее представление в его случае будет оптимальным/разумным. С вопросом в какой момент и каким способом нужно округлять результаты расчетов обращаться конечно же нужно к клиенту.
·>Да это не от страны зависит, вроде. Например, продажа по цене $99.99/кг, и продав 10кг сотне клиентов это не то же самое что продажа 100кг десяти.
Разумеется не то же самое. НДС считается отдельно по каждому документу. Оно наверно везде так. В России и отдельно по каждой строке (наименованию товара) документа. Ставки могут быть разные. Но не на единицу товара, а на все количество. Тут не возьмусь судить, как в других странах. Еще в России есть особенности связанные с заполнением налоговой декларации, но там возможные небольшие отклонения связанные с округлениями допустимы и никого не волнуют.
Здравствуйте, ·, Вы писали:
·>Да, в спеках UK-налоговой там шесть видов округлений: до пенсов, до фунтов * вверх, вниз, банковское.
·>Вопрос — возможно ли реализовать эти округления используя тип double?
Любое округление до доли размером P делается в случае идеально точных расчётов по формуле P*ROUND(val/P), где val — исходное значение, ROUND — конкретная функция округления до целого.
Как называется эта функция — зависит от языка.
В случае C: round() округляет до ближайшего, а половинки — в направлении от нуля; nearbyint() — в соответствии с текущим направлением (по умолчанию это к ближайшему и банкирское); rint() — то же, что nearbyint(), но поднимая inexact (для данной задачи явно не нужно); floor(), ceil() — любые с ненулевой целой частью. trunc() аналогично floor() для положительных и ceil() для отрицательных, отдельно не вспоминаем. Также nearbyint() при FE_DOWNWARD аналогично floor(), а при FE_UPWARD — ceil().
Пример: входное значение и результат округления; NB/RNE — nearbyint() при округлении к ближайшему (умолчание FE_TONEAREST для IEEE).
Если сумма в double в фунтах, то соответственно получается (но см. уточнение ниже):
для фунтов — ROUND(val); для пенсов — ROUND(val*100)/100.
Вверх — ROUND(x) = ceil(x); вниз — ROUND(x) = floor(x). Банковское — только NB/RNE.
Тут, однако, есть одна тонкость. Представим себе, что в результате некоторой операции у нас получилась сумма в фунтах вида 12.0299999999999999. Она должна восприниматься как 12.03 перед всеми воздействиями! Оба округления вверх и вниз до центов должны дать 12.03, хотя внутри это снова может оказаться 12.0299999999999999 или 12.03000000000002. Ещё хуже, если мы попытаемся округлять какое-нибудь 11.99999999999999 вниз до целых фунтов, получив 11, когда на самом деле нужно 12.
Поэтому: обычно говорят, что многократное округление — это очень вредно, приводя пример ситуацйй вида "1.453 сначала округлили до сотых до 1.45, а потом до десятых банкирским до 1.4; сразу до десятых это дало бы более корректное 1.5"; аналогично при правиле типа "0.5 округляется вверх" 1.47->1.5->2 вместо 1.47->1". Но в случае финансов, наоборот, необходимо двойное округление! (Но не более) 11.99999999999999 до фунтов — сначала должно быть округлено до центов — до 12.00, а потом уже смотреть, куда его и как округлять; иначе округление вниз до целых фунтов даст 11, а не правильное 12. А так как округление до центов это nearbyint(x*100)/100, то может оказаться, что лучше провести в целых всю цепочку округления, а не одну операцию (а то и вообще перейти на fixed).
Здравствуйте, netch80, Вы писали:
n> Поэтому: обычно говорят, что многократное округление — это очень вредно, приводя пример ситуацйй вида "1.453 сначала округлили до сотых до 1.45, а потом до десятых банкирским до 1.4; сразу до десятых это дало бы более корректное 1.5"; аналогично при правиле типа "0.5 округляется вверх" 1.47->1.5->2 вместо 1.47->1". Но в случае финансов, наоборот, правильно двойное округление! 11.99999999999999 до фунтов — сначала должно быть округлено до центов — до 12.00, а потом уже смотреть, куда его и как округлять; иначе округление вниз до целых фунтов даст 11, а не правильное 12. А так как округление до центов это nearbyint(x*100)/100, то может оказаться, что лучше провести в целых всю цепочку округления, а не одну операцию (а то и вообще перейти на fixed).
А почему первое округление именно до пенсов? По-моему логичнее на знак меньше, т.е. до 0.1 фунта.
Т.е. я правильно понял, что технически невозможно считать всё в double с точным соблюдением финансовых правил? Без fixed не обойтись никак?
Здравствуйте, pagid, Вы писали:
p> ·>Тогда он должен обсуждать с клиентами как они думают должно вычисляться чтобы они были довольны. p> ТС с нам обсуждал вопрос реализации не касающийся пользователей — какое внутреннее представление в его случае будет оптимальным/разумным. p> С вопросом в какой момент и каким способом нужно округлять результаты расчетов обращаться конечно же нужно к клиенту.
Мне кажется, что перечисленные варианты будут выдавать разные результаты, как раз из-за округлений, вызванных потерями точности. Поэтому тут не в оптимальности вопрос, а в корректности результатов. Хотя, конечно, если клиентов устроят любые более-менее правдоподобные результаты, то я бы считал в double — проще и быстрее. Но где найти таких клиентов...
Здравствуйте, ·, Вы писали:
·>Т.е. я правильно понял, что технически невозможно считать всё в double с точным соблюдением финансовых правил? Без fixed не обойтись никак?
Считать можно, перед выводом на экран/на печать/сохранением в БД, если округление специфическое танцы с бубном могут потребоваться.
Здравствуйте, ·, Вы писали:
·>Мне кажется, что перечисленные варианты будут выдавать разные результаты, как раз из-за округлений, вызванных потерями точности.
Не должны. Это его задача, и представление чисел выбирается в том числе с этой целью.
·> Поэтому тут не в оптимальности вопрос, а в корректности результатов. Хотя, конечно, если клиентов устроят любые более-менее правдоподобные результаты, то я бы считал в double — проще и быстрее. Но где найти таких клиентов...
Неплохо занаком с учетно-бухгалтерской системой вполне успешно существующей уже лет 20, внутреннее представление денег — double, пользователей системы, как конечных, так и пишущих на прилагаемом скриптовом языке это ничуть не волнует и источником проблем не является.
Здравствуйте, ·, Вы писали:
n>> Поэтому: обычно говорят, что многократное округление — это очень вредно, приводя пример ситуацйй вида "1.453 сначала округлили до сотых до 1.45, а потом до десятых банкирским до 1.4; сразу до десятых это дало бы более корректное 1.5"; аналогично при правиле типа "0.5 округляется вверх" 1.47->1.5->2 вместо 1.47->1". Но в случае финансов, наоборот, правильно двойное округление! 11.99999999999999 до фунтов — сначала должно быть округлено до центов — до 12.00, а потом уже смотреть, куда его и как округлять; иначе округление вниз до целых фунтов даст 11, а не правильное 12. А так как округление до центов это nearbyint(x*100)/100, то может оказаться, что лучше провести в целых всю цепочку округления, а не одну операцию (а то и вообще перейти на fixed). ·>А почему первое округление именно до пенсов? По-моему логичнее на знак меньше, т.е. до 0.1 фунта.
Тогда пусть у тебя 11 фунтов 97 пенсов, а надо округлить вниз до целых фунтов, а число в double равен 11.969999999999999999995. Если по-моему — округляем до пенсов — получаем 1197 пенсов, и затем от этого идём вниз до 11 фунтов. Если же ты округлишь до 0.1 фунта, оно сразу поднимется до 12.00. И затем до фунтов останешься на 12, а не 11.
·>Т.е. я правильно понял, что технически невозможно считать всё в double с точным соблюдением финансовых правил? Без fixed не обойтись никак?
Я не говорил — невозможно, я говорил — может быть лучше перейти на fixed.
Все эти trunc, round, floor, ceil, nearbyint выдают результат того же типа (float, double), но точно равный целому (предположим, что потери точности целого уже не происходит). Но, умножая или деля обратно для приведения в исходный масштаб, ты снова вносишь особенности двоичного представления (оно там будет "в душе" каким-нибудь 11.9700000000000000003).
Все рабочие значения у тебя (или ТС) в таком варианте — точки равномерной сетки с шагом, например, 0.01 (если это пенсы при фунтах), но из-за двоичного представления она каждая чуть смещена относительно точного, не представимого тут, значения. При "вытаскивании" в целые — например, при перемасштабировании в шаг 1 умножением на 100 — надо привести округлением к ближайшему к точному значению узла сетки с шагом 1. Это всё, что тут однозначно требуется, остальное уже зависит от дальнейших действий. А если этот по-сути-fixed представляется в double — с этим вполне можно работать (тем более, что при целых числах прежних привычных ошибок в мелких разрядах уже нет).
Здравствуйте, кт, Вы писали:
кт>Как раз я ей не занимаюсь и не пытаюсь использовать для финансовых расчетов мантиссу и порядок.
Тогда чем не устраивает обычный integer? Можно условится, что 1 это одна копейка, сотая или, если угодно, миллионная доля копейки и считать в фантастически быстром integer без всяких мантисс и порядков.
кт>А пример с 1/3 непоказателен. В большинстве случаев в финансах нужны не рациональные дроби, а проценты (и особенно сложные проценты). Как раз они прекрасно представляются в десятичном виде.
Очень даже показателен, в финансовых расчетах. 18% "НДС включая" с 10 руб. уже периодическая дробь, и подобное там спошь и рядом. Сумму авансовых платежей по налогу нужно разделить равными долями на три месяца квартала....
кт> Автор старой статьи пытался донести для чего в IA-32 введена двочная арифметика, Float-арифметика и BCD-арифметика.
Про этот калькуляторный рудимент и атавизм netch80 уже написал.
кт>P.S. в статье объяснялась, чем не устраивает Decimal С#
Плохо там объясняется, совершенно без аргументации.
Да, decimal — не BCD, но в пределах своего диапазона значений, более чем достаточного для финансовых расчетов, decimal позволяет точно представить все значения точно представляемые в десятичной системе.
... << RSDN@Home 1.2.0 alpha 5 rev. 1495>>
Re[9]: предлагаю ознакомиться со статьей на соседней ветке
Здравствуйте, pagid, Вы писали:
P>Тогда чем не устраивает обычный integer? Можно условится, что 1 это одна копейка, сотая или, если угодно, миллионная доля копейки и считать в фантастически быстром integer без всяких мантисс и порядков.
опять снова здорово.
Подумайте, а почему 60 лет назад так не делали?
Целые как миллионные доли копейки — это расчет с точностью до миллионный.
Вместо этого был введен аппарат точных расчетов для десятичных дробей, который многими почему-то считается какой-то дикой ерундой. Если это ерунда — раскройте глаза фирме IBM, это они придумывали.
Re[10]: предлагаю ознакомиться со статьей на соседней ветке
Здравствуйте, кт, Вы писали:
P>>Тогда чем не устраивает обычный integer? Можно условится, что 1 это одна копейка, сотая или, если угодно, миллионная доля копейки и считать в фантастически быстром integer без всяких мантисс и порядков. кт>опять снова здорово. кт>Подумайте, а почему 60 лет назад так не делали?
Делали. Именно так и делали. Там, где вы видите какой-нибудь fixed decimal(10,2) в PL/1, "внутре" была целочисленная арифметика и сдвиг порядка там, где это нужно, уже при интерпретации данных. Разница в том, что в тех языках подробности скрывались от прикладников, а с новой волной, пришедшей из системного программирования и основанной в первую очередь на C, потроха оказались вывернуты наружу.
А матфизика никогда не интересовалась десятичными, зато ей была важна максимальная точность и скорость вычислений в мире реальных цифр, которые никогда не точны до конкретной цифры, что тянуло именно в сторону двоичной арифметики.
кт>Целые как миллионные доли копейки — это расчет с точностью до миллионный. кт>Вместо этого был введен аппарат точных расчетов для десятичных дробей, который многими почему-то считается какой-то дикой ерундой. Если это ерунда — раскройте глаза фирме IBM, это они придумывали.
Вы не заметили, что это нововведение IBM — это уже 2000-е годы! До этого decimal floating в железе был очень тяжелоподъёмным и, как следствие, не было стандарта.
Точно так же как исходный IEEE754-1985 вошёл в силу только тогда, когда стало жечь уже невыносимо (а почему так см. подробности бардака на рынке), и несколько фирм (начиная с Intel и Motorola) сделали и отшлифовали аппаратные реализации (передирая особенности друг у друга, начиная с внутреннего 80-битного представления и продолжая абсолютно тупыми решениями вроде переноса denormals на микропрограмму — только сейчас со скрипом избавились от этого, и то наполовину), так и сейчас только стабилизация концепций позволила рискнуть сделать аппаратную реализацию, отработать её и тогда пропихнуть в стандарт.
До этого decimal floating был такой же областью неразберихи, как binary до IEEE754 (и мы видим это на примере дотнетовского decimal). Только в 80-х было порождено первое предложение, которое ещё потом долго и задумчиво шлифовали.
(Судя по всему, про то, что это IBM, Вы прочли у меня. Но проверить хоть чуть-чуть историю и сверить хронологию даже не пытались.)
(И ещё заметьте, что decimal floating в железе пока ни у кого кроме IBM нет, и то — есть только на системах, которые нацелены именно на финансовый рынок — zSeries уходит в основном туда. Остальным не припекло.)
Здравствуйте, FrozenHeart, Вы писали:
FH>Скорее, пример должен быть такой:
FH>
FH>double number = 0.1 + 0.1 + 0.1;
FH>if (numbers == 0.3) { /* do smth */ }
FH>
FH>Вот такие ситуации в случае decimal floating point numbers разве могут когда-то зафейлиться?
1.0/3.0 + 1.0/3.0 + 1.0/3.0 == 1.0 может зафейлиться. Без потери точности мы можем делить только на степени двойки и пятёрки, и то в некоторых пределах.
Здравствуйте, pagid, Вы писали:
Ops>>А еще, например, авансовые платежи в налоговую округляются не до копеек, а до целых рублей. P>И все налоги уже давно округляются до рублей. При этом рекомендуется использовать обычное арифметическое округление.
Там с авансовыми платежами хитрость в том, что ты платишь каждый раз за все предыдущие периоды, вычитая уже уплаченное. Т.е. ошибка округления за год != сумме ошибок всех платежей за год.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Здравствуйте, Ops, Вы писали:
Ops>Там с авансовыми платежами хитрость в том, что ты платишь каждый раз за все предыдущие периоды, вычитая уже уплаченное. Т.е. ошибка округления за год != сумме ошибок всех платежей за год.
За год не будет ошибки. А пример только о том, что на три иногда приходится делить.