Существует ли единое представление данных, которое работает для всех валют (даже отличных от долларов, евро и фунтов)?

12

Я могу найти много вопросов о библиотеках для представления сумм в какой-то валюте. И о давней проблеме, почему вы не должны хранить валюту как число с плавающей точкой IEEE 754. Но я не могу найти больше ничего. Конечно, есть еще много информации о валюте в реальном мире. Меня особенно интересует, что вам нужно знать, чтобы представить это в физическом выражении (например, с долларом вы никогда не будете иметь точность менее 0,01 доллара, что позволяет представлять его как целое число центов).

Но трудно предположить, насколько универсальной является ваша программа, когда единственными валютами, которые вы знаете, являются популярные западные (например, доллар, евро и фунт). Что еще относится к знаниям в чисто программной перспективе? Меня не волнует тема конверсии.

В частности, что нам нужно знать, чтобы иметь возможность хранить значения в некоторой валюте и распечатывать их?

Kat
источник
2
Не все программисты работают в отраслях, которые манипулируют суммами в валюте.
whatsisname
4
О Конечно. Но это можно сказать почти обо всем. Я думаю, мы можем предположить, что те, кто посчитает этот вопрос полезным, будут работать с валютой.
Кат
5
Я проделал большую работу по разработке для банков и других финансовых компаний в Нью-Йорке. И я могу в значительной степени гарантировать вам, что на ваш вопрос нет ответа в «закрытой форме». Вы могли бы также спросить, "сколько математики я должен знать?" Например, в 1990-х годах цены на акции выросли с восьмых и 256-х до десятичных, что потребовало большого количества перепрограммирования. Кто бы мог подумать, что это произойдет? Вам просто нужно понять бизнес, который поддерживают ваши приложения, и как лучше всего представлять данные (в данном случае валюту) для удовлетворения этих бизнес-потребностей.
Джон Форкош
4
Мой 0.02: цена это одно. Валюта это другое.
Мачадо
3
Я подумал: «О, должен быть какой-то общий знаменатель для всех валют». Но, возможно, нет. Пенни-стерлинг в настоящее время составляет 1⁄100 фунта, но составлял 1⁄240 фунта. Таким образом, у вас есть не только деление 1/240, но у вас есть дата переключения и, возможно, обменный курс для старых пенсов, которые еще не были переключены. en.wikipedia.org/wiki/Penny_sterling Даже не заводи меня на каури! en.wikipedia.org/wiki/Shell_money или тот факт, что деньги требуют веры, чтобы стать реальными: en.wikipedia.org/wiki/Money Отличный вопрос, даже если он не подходит StackExchange!
ГленПетерсон

Ответы:

24

например, с долларом вы никогда не будете иметь точность менее 0,01 доллара

Да неужели? введите описание изображения здесь

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

введите описание изображения здесь

Пожалуйста, не стесняйтесь хранить дюймы в числах с плавающей точкой IEEE 754 . Они хранят именно то, что вы ожидаете.

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

Почему? Потому что, когда вы используете IEEE 754 , вы храните его.

Что касается дюймов, они разделены пополам. Что касается большинства видов валюты, то они разделены на десятые доли (некоторые - нет, но давайте сосредоточимся).

Эта разница не будет слишком запутанной, за исключением того, что для большинства языков программирования ввод и вывод из чисел с плавающей запятой IEEE 754 выражается в десятичных числах! Что очень странно, потому что они не хранятся в десятичных числах.

Из-за этого вы никогда не увидите, как эти биты делают странные вещи, когда просите компьютер сохранить 0.1. Вы видите странность только тогда, когда вы делаете математику против нее, и в ней есть странные ошибки.

От эффективного Java Джош Блоха :

System.out.println(1.03 - .42);

Производит 0.6100000000000001

Что больше всего говорит об этом, так это не 1сидение справа там. Это странные цифры, которые должны были быть использованы, чтобы получить его. Вместо того, чтобы использовать самый популярный пример, 0.1мы должны использовать пример, который показывает проблему и избегает округления, которое скрыло бы ее.

Например, почему это работает?

System.out.println(.01 - .02);

Производит -0.01

Потому что нам повезло.

Я ненавижу проблемы, которые трудно диагностировать, потому что иногда мне везет.

IEEE 754 просто не может хранить 0.1 точно. Но если вы попросите его сохранить 0,1, а затем попросите его напечатать, он покажет 0,1, и вы будете думать, что все в порядке. Это не хорошо, но вы не можете видеть это, потому что округляется, чтобы вернуться к 0,1.

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

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

Никто не ожидает, что π будет хранить именно в калькуляторе, и им удается нормально с ним работать. Так что проблема даже не в точности. Это об ожидаемой точности. Компьютеры отображают одну десятую, как 0.1и наши калькуляторы, поэтому мы ожидаем, что они будут хранить одну десятую точно так же, как наши калькуляторы. Они не Что удивительно, так как компьютеры стоят дороже.

Позвольте мне показать вам несоответствие:

введите описание изображения здесь

Обратите внимание, что 1/2 и 0.5 идеально выровнены. Но 0.1 просто не совпадает. Конечно, вы можете приблизиться, если продолжите делить на 2, но вы никогда не попадете точно. И нам нужно все больше и больше битов каждый раз, когда мы делим на 2. Поэтому для представления 0.1 в любой системе, которая делит на 2, нужно бесконечное количество битов. Мой жесткий диск не такой большой.

Поэтому IEEE 754 перестает пытаться, когда заканчивается бит. Что хорошо, потому что мне нужно место на моем жестком диске для ... семейных фотографий. Нет, правда. Семейные фотографии. :П

В любом случае, то, что вы вводите, и то, что вы видите, - это десятичные дроби (справа), а то, что вы сохраняете, - это двоичные числа (слева). Иногда они совершенно одинаковы. Иногда это не так. Иногда это выглядит так, будто они одинаковы, а просто нет. Это округление.

В частности, что нам нужно знать, чтобы иметь возможность хранить значения в некоторой валюте и распечатывать ее?

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

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

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

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

введите описание изображения здесь

candied_orange
источник
6
Я действительно не искал еще одного объяснения того, почему IEEE 754 не работает за деньги. Я упомянул в OP, что это было хорошо объяснено в другом месте. Точно так же есть причина, по которой я использовал термин «физическое использование». А именно потому, что, хотя цены на газ, биржевые цены и т. Д. Могут иметь произвольную точность, физические деньги (и огромное количество пользовательских транзакций) не проходят сотни мест (а различные части программного обеспечения хотят представлять только то, за что можно заплатить). физические деньги). Одна часть информации, которая меня удивила, заключалась в том, что существуют ли валюты, которые идут в более десятичных разрядах.
Kat
3
«Странность» с плавающей запятой не ограничивается только половиной против 1/10. Аспект «с плавающей запятой» в имени также вызывает иногда неожиданные результаты.
whatsisname
6
До десятичного знака британская валюта составляла фунты, шиллинги и пенсы (£ sd), с £ 1 == 20 с, 1 с = 12d, поэтому £ 1 = 240d, поэтому 1d = 0,0046666666666 .. поэтому ее нельзя было представить в виде десятичной дроби. До присоединения к евро итальянская лира превысила 2346,4 (в 2001 году) по отношению к доллару США, поэтому такие вещи, как зарплаты и цены на жилье иногда достигают верхнего предела с плавающей запятой одинарной точности, а экономические показатели достигают верхних уровней двойных значений.
Стив Барнс
2
Я бы сказал, что валюта - это одно, а цена - другое. Вы можете иметь точность цен более 0,01 доллара, но вы не можете эффективно платить с такой точностью.
Мачадо
1
Также семейные фото . Pft. :)
Мачадо
10

Я могу найти много вопросов о библиотеках для представления сумм в какой-то валюте.

Как я объясню, «библиотеки» не нужны, если в некоторых типах данных отсутствует стандартная библиотека вашего языка.

Конечно, есть еще много информации о валюте в реальном мире. Меня особенно интересует, что вам нужно знать, чтобы представить это в физическом выражении (например, с долларом вы никогда не будете иметь точность менее 0,01 доллара, что позволяет представлять его как целое число центов).

Проще говоря, вам нужно десятичное число с фиксированной запятой, а не десятичное с плавающей запятой. Например, класс Java BigDecimal можно использовать для хранения суммы в валюте. Другие современные языки имеют подобные встроенные типы, включая C # и Python . Реализации различаются, но обычно они хранят число в виде целого числа с десятичным местоположением в качестве отдельного элемента данных. Это дает точную точность даже при выполнении арифметических операций, которые дают странные остатки (например, 0,0000001) с числами с плавающей запятой IEEE.

В частности, что нам нужно знать, чтобы иметь возможность хранить значения в некоторой валюте и распечатывать ее?

Есть несколько важных моментов.

  1. Используйте фактический десятичный тип, а не с плавающей точкой.

  2. Следует понимать, что сумма в валюте имеет два компонента: значение (5,63) и код или тип валюты (USD, CAD, GBP, EUR и др.). Иногда вы можете игнорировать код валюты, в других случаях это жизненно важно. Что делать, если вы работаете в финансовой или розничной системе электронной коммерции, которая допускает несколько валют? Что произойдет, если вы пытаетесь взять деньги у клиента в CAD, но он хочет заплатить с помощью MXN? Вам нужен тип «деньги» с кодом валюты и суммой валюты, чтобы иметь возможность смешивать эти значения (также обменный курс, но я не хочу слишком углубляться в касательную). В то же время моему программному обеспечению для личных финансов никогда не нужно беспокоиться об этом, потому что все в долларах (оно может смешивать валюты, но мне это никогда не нужно).

  3. В то время как валюта может иметь наименьшую физическую единицу на практике (CAD и USD имеют центы, JPY - это просто ... Йена), возможно, станет меньше. Ответ CandiedOrange указывает цены на топливо в десятых долях цента. Мои налоги на недвижимость оцениваются в мельницах за доллар или в десятых долях цента (1/1000 доллара США). Не ограничивайте себя $ 0,01. В то время как вы можете отображать эти значения большую часть времени, ваши типы должны разрешать меньшие (десятичные типы, упомянутые выше, делают).

  4. Промежуточные вычисления, безусловно, должны обеспечивать большую точность, чем один цент. Я работал над системами розничной торговли / электронной коммерции, где внутренние значения были округлены до 0,00000001 долл. США внутри страны. Бесконечная точность обычно не поддерживается десятичными типами (или SQL), поэтому должен быть некоторый предел. Например, деление 1/3 с использованием Java BigDecimal вызовет исключение без указания RoundingMode или MathContext, поскольку значение не может быть точно представлено.

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

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


источник
На 2, я думаю, что обменный курс достаточно, чтобы получить хотя бы его собственный номер. Вы должны учитывать, что ставки меняются со временем, что имеет несколько разных способов обработки.
Изката
2
+1. Кроме того, валюты меняются со временем. В Бразилии мы неоднократно меняли валюту в течение 80-х и 90-х годов, сокращая нули и переименовывая валюту при необходимости из-за неконтролируемой инфляции. Это означает, что каждый аспект валюты может меняться со временем.
Мачадо
@Izkata, конечно, я работал над системами, которые меняют валюты. Я хотел коснуться темы, так как она актуальна. Однако этот вопрос не является предметом обсуждения, и я мог бы, вероятно, напечатать другой ответ, если только этот на тему обмена валюты.
«Библиотеки не нужны, если в некоторых типах данных нет стандартной библиотеки вашего языка, как я объясню». Валюты могут иметь половинки, четверти, десятые, восьмые и т. Д., Что означает, что нам по крайней мере нужно десятичное представление для них. Но полностью ли мы уверены, что не существует валют с 1/3 от общей суммы?
Даниэль Маклаури
4

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

Вы должны знать, что цифры могут быть большими. Есть валюты, где эквивалент, скажем, десять долларов составляет более 100 000 единиц. И нам повезло, что в данный момент нет таких валют, как зимбабвийский доллар, где вы могли бы иметь банкноту в сто триллионов!

Для отображения валют вам понадобится некоторая библиотека - у вас нет шансов все сделать правильно. Отображение зависит от двух вещей: кода валюты и языкового стандарта пользователя. Подумайте, как будут отображаться доллары США и канадские доллары в локали США и Канаде: в США у вас есть $ против CAN $, а в Канаде у вас есть $ против $. Надеюсь, это встроено в ОС, или у вас есть хорошая библиотека.

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

gnasher729
источник
0

В частности, что нам нужно знать, чтобы иметь возможность хранить значения в некоторой валюте и распечатывать их?

  • «хранить значения» : тип данных с фиксированной точкой и тип валюты. Я думаю, что это довольно очевидно. Хранение до числа с фиксированной запятой может включать некоторое округление и изменение значения. Расчет валюты будет другой проблемой.
  • «распечатать их» : локаль пользователя. Если вам повезет, вы получите стандартную библиотеку, которая сделает все, например, символ валюты, разделитель тысяч, десятичный разделитель, точность и т. Д.

Некоторые примеры проблем, связанных с локалью:

  • 100 долларов США в США должны составлять 100 долларов США, в других странах с точкой в ​​качестве десятичного разделителя будет 100 долларов США.
  • Некоторые страны используют множество вместо тысяч для группировки номеров, например, вместо 10 000,00 это будет просто 1,0000,00
  • По практическим причинам люди не печатают все цифры для небольших валют, например, вместо 1 000 000 000,00 долларов, они просто хотят, чтобы вы напечатали 1 миллиард долларов.

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

imel96
источник