Если пользователь хочет «сравнить два числа с плавающей запятой, используя заданное количество десятичных знаков (значащих цифр)», и это на самом деле означает, что у нас есть функция
Почти Равно(14.3XXXXXXXX, 14.3YYYYYYY, 1) == true для всех возможных XXX и YYY, а последний параметр — десятичный знак после запятой.
есть простой, но неудачный ответ:
НЕвозможно запрограммировать эту функцию, которая будет выполнять этот контракт. Можно запрограммировать что-то, что часто будет давать правильный результат, но вы не можете предвидеть, когда это произойдет, поэтому функция фактически бесполезна.
Приведенные здесь решения уже нарушают почти равное (0.06f, 0.14f, 1) = true, но 0 != 1.
Почему ? Первая причина – крайняя чувствительность. Например: 0,0999999999.... и 0,100000...1 имеют во-первых разные цифры, но в разнице они почти неразличимы, они почти точно равны. Что бы ни делала мифическая функция, она не может допустить даже небольших различий в расчетах.
Вторая причина заключается в том, что мы хотим на самом деле вычислить с помощью чисел. Я использовал VC 2008 с C#, чтобы распечатать правильные значения функции Math.pow. Первый — это параметр точности, второй — шестнадцатеричное значение результирующего числа с плавающей запятой, а третий — точное десятичное значение.
1 3dcccccd 0.100000001490116119384765625
2 3c23d70a 0.00999999977648258209228515625
3 3a83126f 0.001000000047497451305389404296875
4 38d1b717 0.0000999999974737875163555145263671875
5 3727c5ac 0.00000999999974737875163555145263671875
6 358637bd 9.999999974752427078783512115478515625E-7
Как видите, последовательность 0,1, 0,01, 0,001 и т. д. дает числа, которые являются отличными приближениями, но либо слишком маленькими, либо слишком большими.
Что, если мы заставим данное место иметь правильную цифру? Давайте перечислим 16 двоичных значений для 4 бит.
0.0
0.0625
0.125
0.1875
0.25
0.3125
0.375
0.4375
0.5
0.5625
0.625
0.6875
0.75
0.8125
0.875
0.9375
16 различных двоичных чисел должно быть достаточно для 10 десятичных чисел, если мы хотим вычислять только с одним знаком после запятой. В то время как 0,5 точно равно, принудительное применение одной и той же десятичной цифры означает, что для 0,4 требуется 0,4375, а для 0,9 требуется 0,9375, что приводит к серьезным ошибкам.
Нарушение первого условия предельной чувствительности означает, что с такими числами нельзя сделать ничего разумного. Если бы вы знали, что десятичный разряд числа имеет определенное значение, вам вообще не нужно было бы вычислять.
В документации C# даже приводится пример: http://msdn.microsoft.com/en-us/library/75ks3aby.aspx
Примечания для вызывающих абонентов
Из-за потери точности, которая может возникнуть в результате представления десятичных значений в виде чисел с плавающей запятой или выполнения арифметических операций со значениями с плавающей запятой, в некоторых случаях метод Round(Double, Int32) может не округлять средние значения до ближайшего четного числа. значение в десятичном разряде цифр. Это показано в следующем примере, где 2,135 округляется до 2,13 вместо 2,14. Это происходит из-за того, что внутри метод умножает значение на 10 цифр, и операция умножения в этом случае страдает от потери точности.
person
Thorsten S.
schedule
09.02.2012
Math.Round()
выдает исключение, если точность меньше нуля или больше 15. - person Igor Korkhov   schedule 07.02.2012123.45
было равно678.45
? - person Mr Lister   schedule 07.02.2012123.123
почти равно123.120
, но123.124
не почти равно123.126
(поскольку последнее округляется до123.13
)? - person Mr Lister   schedule 07.02.2012AlmostEquals(0.149f, 0.151f, 1)
? - person CodesInChaos   schedule 19.03.2012