2010-07-01 7 views
10

No sé por qué el techo se comporta como en la imagen de abajoC# Math.Ceiling bug o no?

¿Por qué está processingFee! = Settings.PaymentProcessingFeeInPercentage * premios¿Sum?

View image at full size

alt text http://img514.imageshack.us/img514/3950/csharpceilingproblem.png

+0

yo no entiendo su pregunta, pero asegúrese de que Math.Ceiling() quiere un parámetro decimal o doble –

+0

¿Cuál es el problema aquí? processingFee equivale a 131, lo que parece correcto. –

+0

@Serkan - Math.Ceiling acepta decimal y doble. – GenericTypeTea

Respuesta

25

Su porcentaje no es en realidad 0,05. Es un valor cerrar a 0.05 ... y probablemente un poco más de 0.05. Por lo tanto, cuando se multiplica por 2600, obtienes un valor de poco más de 130.0 ... que luego se "ceilinge" a 131.0.

Utilizando una pequeña herramienta que escribí hace un tiempo (disponible en this page sobre los tipos de punto flotante binario .NET) parece que el valor real float más cercano a 0,05 es 0,0500000007450580596923828125. Para dobles, es 0.05000000000000000277555756156289135105907917022705078125.

Moral de la historia: no use float para este tipo de cosas - use decimal. O si solo está tratando de representar un porcentaje, si está bien que realmente tenga una precisión de uno por ciento, use un valor entero de 0-100.

+2

+1 para sugerir 'Decimal' – Bobby

+0

funcionará con decimal, e incluso el doble para todas las variables –

+1

@ pixel3cs: No, no use' double', tampoco. Tiene los mismos problemas que 'flotar '. La razón por la que 'decimal' evita estos problemas se debe a que es un número base de coma flotante 10 en lugar de un número base de coma flotante 2. En general, 'decimal' va a ser su opción más segura para trabajar con dinero, porque la representación interna de' decimal' coincide con lo que está tratando de representar (aunque de manera menos eficiente). – Brian

4

Este es el resultado de la representación de coma flotante de los números involucrados. See the wikipedia. Probablemente 0.05 tiene un infinito representación de base 2 como un doble, por lo que el valor Math.Ceiling ve realmente podría ser ligeramente mayor que 130.

+0

Para ser absolutamente irritantemente preciso: un entero pequeño (matemático) como 130 tiene una representación finita precisa en números de punto flotante IEEE (ya sea de 32 bits o de 64 bits). Lo que puede no ser representable en números de coma flotante es el número real (matemático) cercano a 130 que OP piensa que es igual a 130 –

+0

@Mark: Tienes razón, por supuesto. 130 = 0.10000010 x 2^8 =) – Jens

2

Estás viendo floatng puntos imprecisión.
La real base 2 representación de 0.05 es un poquito más de 0.05, por lo que el producto es un poquito más de 130.0.
Por lo tanto, Math.Ceiling se está redondeando.

Cambie su float sy double s a decimal.

1

Esto es debido al formato de almacenamiento interno para un número de coma flotante de ser inherentemente inexacto cuando el número está representado en decimal. Hay muchas, muchas preguntas sobre esto en Stack Overflow.

El número va a devolver es probablemente algo así como 130.000000000000001 ya que los números en su cálculo no se pueden representar exactamente como un número de punto flotante binario.

+1

Um, 130 puede * ciertamente * estar representado exactamente como un número de punto flotante binario. Sin embargo, 0.05 no puede. –

+0

130 es 10000010 en binario y definitivamente se puede representar exactamente como un punto flotante IEEE754. –

+0

D'oh. Por supuesto. –

1

En mi humilde opinión, es probable que tenga algo que ver con la precisión de coma flotante. En otras palabras, 2600 × 0.05 da 130.0000 ... 001 en lugar de 130.

¿Qué pasa si intenta redondear el resultado primero, que llamar al Math.Ceiling?

0

En mi compilador, cuando el valor de las operaciones de búsqueda se multiplican, se dice 130,00000193715096, por lo que el resultado es Math.Ceiling bien. El problema es la precisión limitada del tipo de datos float.

Trate de usar 'doble' en su lugar, si es posible.

+0

OP está utilizando doble. – maxwellb

+0

@maxwellb: No por el valor de Settings.PaymentProcessingFeeInPercentage. Sin embargo, 'double' todavía daría la respuesta" incorrecta ". Es simplemente inapropiado usar 'float' o' double' aquí. –

+0

Ah sí, gracias. Y sí en el segundo punto, también. Lo que quise decir es "el doble no es más apropiado que flotar". – maxwellb

0

Si usa números en coma flotante en una operación bancaria grande, no permita que flote mi dinero en su banco.Use decimales, o enteros del mínimo común denominador, es decir, centavos.

Sin embargo, puede usar un Math.Round, para ayudarlo a usar double so float s, si hace suposiciones sobre la cantidad de cálculos que obtendrá. es decir .:

double processingFee = Math.Ceiling(Math.Round( 
    Settings.PaymentProcessingFeeInPercentage * prizesSum, 2)); 
+1

Tenga en cuenta que 'decimal' en C# sigue siendo un tipo de coma flotante. Es solo un punto flotante * decimal * en lugar de un punto flotante * binario *. –

+0

Todavía "válido" solo para un cierto número de sigfigs. Buena distinción. – maxwellb

Cuestiones relacionadas