например, с долларом вы никогда не будете иметь точность менее 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, хорошо работают для проектов большего размера. Они просто медленнее, чем нативные типы.
Выяснить, как хранить это только половина проблемы. Помните, что вы также должны иметь возможность отображать его. Хороший дизайн разделит эти две вещи. Настоящая проблема с использованием поплавков заключается в том, что эти две вещи объединены.