Pull to refresh

Comments 20

PL/1 сегодня (специально глянул на календарь и дату публикации), серьезно?

Почти все денежные системы пришли к десятичным дробным частям (копейка = 1/100 рубля и т.п.)

При таком подходе любой INT в условных копейках — самое элементарное решение которое придется только минимально подтьюнить при вычислении процентов и делении.

Ну а в том же ORACLE и других SQL базах данных есть специальные типы под деньги. Ну собственно базы данных они как правило и хранят «деньги». А часто еще и обрабатывают.
придется только минимально подтьюнить при вычислении процентов и делении.

А вот и не минимально. Пример — инвестиции толпы с выплатой процентов. В случае лишних денег — проблемы с законом и репутацией.


Начать изучение можно с парадокса Алабамы.


А ещё есть Биткойн.

инвестиции толпы с выплатой процентов

Выплата процентов подлежит бухгалтерскому учету. Бухучет всегда оперирует минимальным квантов денег в соответствующей национальной валюте, т. е. в современной России — 1 копейка (много-много лет назад это была ¼ копейки). Следовательно, любая выплата процентов должна быть округлена до 1 копейки.
В расчетах копеек участвуют проценты, с точностью до… (подставить значение по вкусу) знака после запятой.
Эти проценты нужно хранить в бухгалтерском учете, наряду с копейками, также без потери точности.
В бухучете процены не хранятся. Бухучет — это учет транзакций с денежной оценкой имущества. Проценты будут храниться в первичной документации, на основе которой происходят изменения имущества. Там их можно хранить как угодно, главное, чтобы они с течением времени самопроизвольно не менялись.
С PL/1 не сталкивался, сейчас много пишу на RPG (на AS/400). Это то, что пришло на смену COBOL для коммерческих расчетов на платформах IBM. COBOL тут тоже поддерживается, но, в отличии от RPG он уже не развивается давно и на нем ничего нового не пишется.

Так вот там два типа данных с фиксированной точкой:

PACKED — фактически тот же FIXED DECIMAL в PL/1, BCD. Аналог в SQL — DECIMAL

и

ZONED — фактически это представление числа в виде строки. Аналог в SQL — NUMERIC

Что же касается представления сумм в миноритарных единицах, то тут ноги растут немного из другого места. Дело в том, что не во всех валютах количество миноритарных единиц в основной равно 100. Есть валюты (бельгийский франк, итальянская лира, японская йена), в которых вообще миноритарных единиц нет. А есть такие (бахрейнский динар, оманский риал, тунисский динар), где 1000 миноритарных единиц в основной.

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

Но для операций с валютами всегда используется арифметика с фиксированной точкой.

В RPG еще есть функция округления %DECH
Спасибо за объяснение текущего состояния. Поскольку эти проблемы решались еще когда не то, что большинства программистов на свете не было, а их родителей еще не было, то, разумеется, задачи были как-то решены. А в PL/1 все тупо перенесли из Кобола и дело с концом.
Сейчас программиста, не сталкивающегося с финансово-экономическими расчетами, легко узнать по предложению хранить все в целых копейках. Им кажется, что деньги только складывают и умножают ))
Как мне сказал один из наших молодых разработчиков — ты начал программировать когда я еще не родился :-)

Просто в банке работаю. Пишу под AS/400 (IBM i нынче которая). Преимущественно на RPG (но и на C/C++ тоже — там, кстати, тип Decimal встроен для этой платформы, это аналог RPGшного PACKED и поддерживается он на уровне собственно системы — весь код работы с ним, я почти уверен, уже ниже уровня SLIC).

А хранится все, действительно «в копейках». Но причина там совсем другая — выше написал. Специально глянул по нашему справочнику валют — не у всех валют в «рубле» 100 «копеек». Есть и 0 (т.е. вообще нет понятия «копейка»), есть и 1000 (этих мало, но они есть).

Соответственно, чтобы понимать о чем речь, мы всегда смотрим сумму, валюту и количество миноритарных единиц для данной валюты по справочнику валют. Но это вся уже для правильного отображения суммы. А сами они хранятся, дай бог памяти, в переменных типа packed(23: 0) (могу соврать в деталях — я последнее время больше по комплаенсу работаю и ядровым функциям, а эта тема все-таки команды системы расчетов — с ними давно уже не работал)

А вообще тут про COBOL уже было. Именно про его арифметику с фиксированной точкой, правда, в разрезе скорости расходимости рекуррентного соотношения Мюллера — на фиксированной точке оно раза в два более устойчиво чем на плавающей.
О, коллега! Я с этим зверем RPG (на AS/400) тоже сталкивался и даже кое что там подправлял. Но было это еще в 2002-2004-х годах. Не думал что сейчас это все еще до сих пор актуально. Но вы меня порадовали — оно оказывается почти так же неубиваемо как Cobol.
Живет и здравствует :-) В LinkedIn есть вполне активные группы разработчиков.
«Столпы» (Клемент, Хатчинсон — который RPGPGM ведет) там тоже присутствуют и активны

У нас точно знаю что на нем сидят Альфа, Райф и Росбанк.

Мы сейчас на версии 7.3, послед НГ обещали накатить TR9 (на бой, на тестовом уже накатили), а ближе к лету — 7.4

Система очень интересная и концептуально цельная. И очень хорошо работает там, где требуется одновременная работа множества заданий.

Так что все нормально :-)
Последний стандарт IEEE754-2008 содержит типы decimal для вычислений. Поэтому и не стоит пользоваться двоично-десятичной арифметикой.

Вот не надо путать читателей, называя десятичное представление с фиксированной точкой "точным".

В тред врывается НДС 20%. Пусть отпускная цена товара 100р. Представьте, пожалуйста, цену без НДС в виде DECIMAL.

Кстати, а как в реальной жизни решают такие проблемы? Округляют до копейки? Берут в партии столько единиц товара, чтобы всё нормально поделилось?
P.S. Забавно — можно подобрать НДС так, чтобы в десятичной системе счисления получались конечные дроби. Например, 25%, 2.4% или 19.20928955078125%

Кстати, а как в реальной жизни решают такие проблемы? Округляют до копейки?

Округляют. Потому что операцию надо отразить в бухгалтерском учете, а там все операции должны быть проведены в рублях и копейках, но не долях копейки. Если введут монеты типа «½ копейки» или «¼ копейки», как это уже было, значит будут округлять то 0,5 или 0,25 копейки.

С НДС факт продажи — это вообще несколько проводок, потому что НДС учитывается отдельно. Каждую проводку тоже надо округлить до копеек.

Кстати, некоторые налоги при начислении и уплате округляют до рубля.

Забавно — можно подобрать НДС так, чтобы в десятичной системе счисления получались конечные дроби. Например, 25%, 2.4% или 19.20928955078125%

Логика НДС другая. Если Вы хотите продать товар за 100 рублей, то сверх этой суммы Вы должны взять с покупателя еще 20% (или 10%, или 0%) — налог-то с оборота берется. А в бюджет нужно заплатить разницу между тем, что Вы взяли с покупателей сверх своей цены, и тем, что Вы сами заплатили сверх цены продавца при покупке сырья.
Цена 1 ед. без НДС — 83,33 руб.
НДС — 16,67 руб.
Итого к оплате: 100,00 руб.

Вот-вот. Всё равно где-то да вылезет округление, так что нелепо надеяться, что представление точное.
Представлять надо не точно, а как налоговая говорит :-D

Представлять надо не точно, а как налоговая говорит

А это не налоговая говорит, а Налоговый Кодекс и документы ЦБ, связанные с денежным обращением. Собственно, это не только в России так, для чего и придумали денежные типы данных для всяких баз данных — чтобы их движки сами ничего не округляли и не повышали/понижали точность представления.
К сожалению, сами названия «FIXED/FLOAT» (вместо «ТОЧНОЕ/ПРИБЛИЖЕННОЕ») не вносят ясности и интуитивно непонятны.

Они интуитивно понятны, если знать, что такое "число с фиксированной точкой" и что такое "число с плавающей точкой". А именно, это сокращения от fixed point и floating point.


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

Вернемся к простейшему примеру точных вычислений, теперь на языке PL/1, добавив деление на ту же константу, чтобы вернуть исходное значение переменной. Вычисления проведем и для типа FIXED DECIMAL и для типа FLOAT(53) – аналога типа «double» в других языках.


Не стоит забывать, что тут мы можем столкнуться с потерей точности. Пример на RPG:

dcl-s fltValue float(8) inz(165.783);
dcl-s pkdValue packed(6 : 3) inz(165.783);
dcl-s zndValue zoned(6 : 3) inz(165.783);

dsply (%char(fltValue));
dsply (%char(pkdValue));
dsply (%char(zndValue));
dsply ('-------------');

fltValue *= 0.137;
pkdValue *= 0.137;
zndValue *= 0.137;
dsply (%char(fltValue));
dsply (%char(pkdValue));
dsply (%char(zndValue));
dsply ('-------------');

fltValue /= 0.137;
pkdValue /= 0.137;
zndValue /= 0.137;
dsply (%char(fltValue));
dsply (%char(pkdValue));
dsply (%char(zndValue));


Результат:

+1.657830000000000E+002
165.783
165.783
-------------
+2.271227100000000E+001
22.712
22.712
-------------
+1.657830000000000E+002
165.781
165.781


Видим расхождение в третьем знаке. Оно обусловлено тем, что после первого умножения мы теряем три знака 0.000271 — они не помещаются в переменную с фиксированными тремя знаками после запятой.

Это то, что следует иметь ввиду при работе с переменными с фиксированной точкой.
Sign up to leave a comment.

Articles