Округление чисел с плавающей запятой с неточным представлением

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

По сути, мы хотим округлить денежные суммы, например 1000000000,555, до 2 десятичных знаков. Однако числовое представление этого числа с плавающей запятой равно 1000000000,5549999, и в результате мы округлим до 1000000000,55, а не до 1000000000,56.

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

Код написан на C и должен запускаться в windows32 / 64 / linux / solaris, поэтому у нас, к сожалению, нет доступа к таким приятным вещам, как тип данных Decimal в .net.

Любой вклад был бы полезен.

Спасибо, Рикард


person Community    schedule 25.08.2009    source источник


Ответы (4)


Правильный способ представления валют чаще всего - использовать целые числа. Например, в зоне евро распространен подход к представлению стоимости в микро-евро (1E-6). Очевидно, вы бы использовали для этого 64-битную математику. Вы бы постоянно использовали это во всем приложении. Только при вводе-выводе человека вы можете округлить, и вы сделаете это, разделив на 10000, чтобы получить целое число в центах.

person MSalters    schedule 25.08.2009
comment
Под собственно вы имеете в виду, что указано государственным органом? Есть ли у вас упоминание о том, что математика с фиксированной запятой - правильный способ сделать это? - person Eric J.; 25.08.2009
comment
+1, но обратите внимание, что часто есть законы или другие правила, которые предписывают способ округления. Прежде чем использовать ту или иную технику, я должен убедиться, что у меня есть доступ к спецификации. - person AProgrammer; 25.08.2009
comment
Общий подход был частью обязательной процедуры конвертации прежних национальных валют в евро. В этом случае это было прямо указано Европейским центральным банком от имени ЕС, IIRC. Это довольно специфично. Во многих других случаях местное законодательство просто требует передовой практики. Это легко, если вы повторно используете существующую обязательную практику. Во-первых, у вас уже может быть с этим опыт, и, во-вторых, в суде трудно оспорить, что правила ЕЦБ не являются хорошей практикой. - person MSalters; 25.08.2009

Мораль этой сказки - никогда не использовать числа с плавающей запятой для обозначения денег!

Деньги поступают дискретными суммами, и это признается в большинстве финансовых и бухгалтерских правил. Вы не можете физически оплатить счет на сумму 3,145217 долларов. В то время как в 17 веке было приемлемо отнести монету кузнецу и заставить его разрезать серебряный доллар на куски (у Pieces of Eight были красивые маркировки на кусочках пиццы, чтобы облегчить этот процесс!), Сегодня это просто невозможно.

Например, самая маленькая монета, доступная в Швейцарии, - это 5 раппов, поэтому бухгалтерский учет и счета должны быть выражены с точностью до 5 центов, то есть вы не можете получить счет за 3,14 швейцарских франков, он должен быть 3,15 швейцарских франков или 3,10 швейцарских франка в маловероятном случае, если ваш поставщик щедрый, потому что вы можете заплатить только 3,10 или 3,15 наличными.

У вас есть два варианта. Получите библиотеку boost BigDecimal, которая позволит вам точно указывать округление. Или, как предлагал другой плакат, используйте long long, представляя ваши суммы в тысячных долях евро и отображая только округление.

Еще одна возможность - использовать единицы полцента, то есть 55 евро 35 центов представлены внутри как 11070 полцента, тогда вам не нужно беспокоиться об округлении для любой стандартной бухгалтерской операции.

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

person James Anderson    schedule 25.08.2009
comment
Это правда лишь отчасти. При ценообразовании деривативов никто не беспокоится о BigDecimal и т. Д., Мы просто используем двойные. - person quant_dev; 30.08.2009

Я рекомендую использовать пакет с десятичным числом, например decNumber.

person Martin v. Löwis    schedule 25.08.2009

Если вы работаете в Windows и хотите написать управляемый C / C ++.

В большинстве случаев вы можете просто использовать десятичную дробь класс. Из MSDN

Тип значения Decimal подходит для финансовых расчетов, требующих большого количества значащих целых и дробных цифр и отсутствия ошибок округления.

Если вы не можете или не хотите использовать управляемый C / C ++, посмотрите пакет десятичных чисел, например decNumber.

В обоих случаях у вас должно быть много модульных и интеграционных тестов, которые охватывают угловые случаи с округлением. Они причинят вам больше всего боли. Также округление очень хорошо помогает скрыть другие ошибки.

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

person Ian Ringrose    schedule 25.08.2009