Si el usuario quiere "comparar dos flotadores usando un número determinado de puntos decimales (cifras significativas)" y en realidad esto significa que tenemos una función
AlmostEquals (14.3XXXXXXXX, 14.3YYYYYYY, 1) == verdadero para todos los posibles XXX y YYY y el último parámetro es el decimal después del punto decimal.
no es un simple pero desafortunada respuesta:
No es posible programar esta función que cumplirá este contrato. Es posible programar algo que a menudo dará el resultado correcto, pero no se puede prever cuándo será el caso, por lo que la función no tiene ningún valor.
Las soluciones que se dan aquí ya romper con AlmostEquals (0.06f, 0.14f, 1) = 0, pero cierto! = 1.
¿Por qué? La primera razón es la sensibilidad extrema. Por ejemplo: 0.0999999999 .... y 0.100000 ... 1 tienen dígitos diferentes en primer lugar, pero son casi indistinguibles en la diferencia, son casi exactamente iguales. Cualquiera que sea la función mítica, no puede permitir incluso pequeñas diferencias en el cálculo.
La segunda razón es que realmente queremos calcular con los números. Utilicé VC 2008 con C# para imprimir los valores correctos de la función Math.pow. El primero es el parámetro de precisión, el segundo el valor hexadecimal del flotante resultante y el tercero es el valor decimal exacto de.
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
Como se puede ver, la secuencia de 0,1, 0,01, 0,001 etc. produce números que son excelentes aproximaciones, pero son o bien ligeramente demasiado pequeño o demasiado grande.
¿Qué pasa si hacemos cumplir que el lugar dado debe tener el dígito correcto? Permite enumerar los 16 valores binarios de 4 bits
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 diferentes números binarios debe ser capaz de ser suficiente para 10 números decimales si queremos calcular únicamente con un solo lugar después del punto decimal. Mientras que 0.5 es exactamente igual, aplicar el mismo dígito decimal significa que 0.4 necesita 0.4375 y 0.9 necesita 0.9375, presentando errores severos.
La violación de la primera condición de sensibilidad extrema significa que no puede hacer nada razonable con tales números. Si supiera que el lugar decimal de un número tiene un cierto valor, no necesitaría calcular en primer lugar.
El # documentación C incluso cita un ejemplo: http://msdn.microsoft.com/en-us/library/75ks3aby.aspx
Notas para los llamadores
Debido a la pérdida de precisión que puede resultar de que representa valores decimales como números de punto flotante o realizar aritmética operaciones en valores de punto flotante, en algunos casos el método Round (Double, Int32) no parece redondear los valores de punto medio al valor más cercano de en la posición decimal de dígitos. Esto se ilustra en el siguiente ejemplo de , donde 2.135 se redondea a 2.13 en lugar de 2.14. Esto ocurre porque internamente el método multiplica el valor por 10 dígitos, y la operación de multiplicación en este caso sufre una pérdida de precisión .
Escribiría un conjunto de pruebas de unidad para verificar si su algoritmo está bien para sus necesidades. –
Me encanta este nombre de método: ** AlmostEquals ** ... – gdoron
He escrito algunas pruebas de unidad y pasan con los valores que he proporcionado, pero me gustaría el consejo de una audiencia con una comprensión más profunda de la flotación implementación/comportamiento. – Kim