Во-первых, вы можете умножать плавающие числа. Проблема заключается не в самом умножении, а в исходном числе, которое вы использовали. Умножение может потерять некоторую точность, но здесь исходное число, которое вы умножили, начало с потерянной точности.
На самом деле это ожидаемое поведение. float
ы реализованы с использованием двоичного представления, что означает, что они не могут точно представлять десятичные значения.
Дополнительную информацию см. в MSDN.
Вы также можете увидеть в описании float, что он имеет точность 6-7 значащих цифр. В вашем примере, если вы округлите 31.099998
до 7 значащих цифр, вы получите 31.1
, так что здесь все работает, как и ожидалось. Тип
double
, конечно, будет более точным, но все же имеет ошибку округления из-за двоичного представления, в то время как число, которое вы написали, является десятичным.
Если вы хотите получить полную точность для десятичных чисел, вам следует использовать тип decimal. Такой тип существует в таких языках, как C#. http://msdn.microsoft.com/en-us/library/system.decimal.aspx
Вы также можете использовать рациональное представление чисел. Использование двух целых чисел даст вам полную точность, пока вы можете представить число как деление двух целых чисел.
Это работает, как и ожидалось. Компьютеры имеют конечную точность, потому что они пытаются вычислить значения с плавающей запятой из целых чисел. Это приводит к неточностям плавающей точки.
На странице Википедии с плавающей точкой гораздо более подробно описано представление и возникающие проблемы с точностью, чем я мог бы рассказать здесь :)
Интересное побочное замечание: это частично является причиной того, почему многие денежные вычисления выполняются с использованием целых чисел (центов) - не позволяйте компьютеру терять деньги из-за недостаточной точности! Я хочу свои $0.00001!
Число 3.11 не может быть представлено в двоичном виде. Самое близкое, что можно получить с 24 значащими битами, это 11.0001110000101000111101, что в десятичной системе равно 3.1099998950958251953125.
Если ваше число 3.11 должно представлять денежную сумму, то вам нужно использовать десятичное представление.
В сообществах Python мы часто видим, как люди удивляются этому, поэтому есть хорошо протестированные и отлаженные разделы FAQs и учебные разделы ] по проблеме (конечно, они сформулированы в терминах Python, а не C, но поскольку Python в любом случае делегирует арифметику с плавающей запятой на базовый C и оборудование, все описания механики с плавающей запятой по-прежнему применимы).
Это, конечно, не вина умножения - удалите оператор, в котором вы умножаете nb
, и вы все равно увидите похожие проблемы.
Из статьи в Википедии :
Тот факт, что числа с плавающей запятой не может точно представить все реальные числа, и что с плавающей запятой операции не могут точно представить истинные арифметические операции, приводит к много удивительных ситуаций. Это связанных с конечной точностью с какие компьютеры обычно представляют числа.
Плавающие точки не точны, потому что они используют основание 2 (потому что оно двоичное: либо 0, либо 1) вместо основания 10. А преобразование основания 2 в основание 10, как многие уже говорили, приведет к проблемам с точностью округления.