2008-11-25 7 views
65

Siempre digo en C# una variable de tipo doble no es adecuada para dinero. Todas las cosas raras podrían suceder. Pero parece que no puedo crear un ejemplo para demostrar algunos de estos problemas. ¿Alguien puede dar ese ejemplo?¿Es un doble realmente inadecuado para el dinero?

(editar; esta publicación se etiquetó originalmente C#; algunas respuestas se refieren a detalles específicos de decimal, que por lo tanto significa System.Decimal).

(edición 2: Estaba específica pidiendo algo de código C#, así que no creo que esto es solo idioma agnóstico)

+0

[¿Por qué no usar Double or Float para representar la moneda?] (Http://stackoverflow.com/q/3730019/995714) –

Respuesta

110

Muy, muy inadecuado. Usa decimal

double x = 3.65, y = 0.05, z = 3.7; 
Console.WriteLine((x + y) == z); // false 

(ejemplo de la página de Jon here - recomienda la lectura de ;-P)

+45

Maldición, si hubiera sabido que tenía un ejemplo en mi propia página, no lo haría. 's han surgido con uno diferente;) –

+7

Pero bueno, 2 ejemplos es mejor que 1 ... –

34

Usted recibirá errores impares efectivamente causadas por el redondeo. Además, las comparaciones con los valores exactos son extremadamente complicadas: por lo general, debe aplicar algún tipo de épsilon para verificar que el valor real esté "cerca" de uno en particular.

Aquí está un ejemplo concreto:

using System; 

class Test 
{ 
    static void Main() 
    { 
     double x = 0.1; 
     double y = x + x + x; 
     Console.WriteLine(y == 0.3); // Prints False 
    } 
} 
+0

Si está consumiendo un servicio que devuelve valores de moneda doble que no puede controlar, hay problemas para pensar en convertirlos a decimal? Pérdida de precisión, etc ... – vikingben

+1

@vikingben: Absolutamente: fundamentalmente, esa es una forma rota de hacer las cosas, y necesita saber cómo interpretar mejor los datos. –

+0

Gracias parece que tengo algo de trabajo que hacer. – vikingben

7

Sí, no es apropiado.

Si mal no recuerdo, el doble tiene alrededor de 17 números significativos, por lo que normalmente los errores de redondeo tendrán lugar muy por detrás del punto decimal. La mayoría del software financiero usa 4 decimales detrás del punto decimal, lo que deja 13 decimales para trabajar, por lo que el número máximo con el que puede trabajar para operaciones individuales es aún mucho mayor que la deuda nacional de EE. UU. Pero los errores de redondeo se sumarán con el tiempo. Si su software se ejecuta durante un tiempo prolongado, eventualmente comenzará a perder centavos. Ciertas operaciones empeorarán esto. Por ejemplo, agregar grandes cantidades a pequeñas cantidades causará una pérdida significativa de precisión.

Se necesitan tipos de datos de punto fijo para las operaciones de dinero, la mayoría de las personas no les importa si se pierde un centavo aquí y allí, pero los contadores no es como la mayoría de la gente ..

edición
De acuerdo con este sitio http://msdn.microsoft.com/en-us/library/678hzkk9.aspx Dobles en realidad tienen de 15 a 16 dígitos significativos en lugar de 17.

@Jon Skeet decimal es más adecuado que el doble debido a su mayor precisión, 28 o 29 decimales significativos. Eso significa menos posibilidades de que los errores de redondeo acumulados se vuelvan significativos. Los tipos de datos de puntos fijos (es decir, enteros que representan centavos o centésimas de centavo como he visto) como las menciones de Boojum son en realidad los más adecuados.

+0

Tenga en cuenta que System.Decimal, el tipo sugerido para usar en .NET, sigue siendo un tipo de punto flotante, pero es un punto decimal flotante en lugar de un punto binario flotante. Eso es más importante que tener una precisión fija en la mayoría de los casos, sospecho. –

+1

Ese es precisamente el problema. La moneda es actualmente típicamente decimal. Antes de que las bolsas de Estados Unidos decimadasen, sin embargo, las fracciones binarias estaban en uso (comencé a ver 256ths e incluso 1024ths en un punto) y por lo tanto los dobles hubieran sido más apropiados que los decimales para los precios de las acciones. Las libras esterlinas antes de la decimación habrían sido un verdadero dolor en 960 fartings por libra; eso no es ni decimal ni binario, pero ciertamente proporciona una generosa variedad de factores primos para fracciones fáciles. –

+1

Aún más importante que simplemente encontrar un punto flotante decimal, 'decimal' la expresión' x + 1! = X' siempre es verdadera. Además, conserva la precisión, por lo que puede diferenciar entre '1' y' 1.0'. – Gabe

5

Dado que decimal usa un factor de escala de múltiplos de 10, los números como 0.1 se pueden representar exactamente. En esencia, el tipo decimal representa esto como 1/10^1, mientras que un double representaría esto como 104857/2^20 (en realidad sería más como realmente grande número/2^1023).

A decimal puede representar exactamente cualquier valor de base 10 con hasta 28/29 dígitos significativos (como 0.1). A double no se puede.

+3

Decimal no tiene 96 dígitos significativos. Tiene 96 significativos * bits *. El decimal tiene alrededor de 28 dígitos significativos. –

+0

¿En qué idioma está hablando del tipo de decimal? ¿O todos los idiomas que admiten este tipo lo soportan exactamente de la misma manera? Podría querer especificar. –

+0

@Adam - esta publicación originalmente tenía la etiqueta C#, por lo que estamos hablando específicamente de System.Decimal. –

4

Según tengo entendido, la mayoría de los sistemas financieros expresan moneda utilizando números enteros, es decir, contando todo en centavos.

precisión doble IEEE en realidad puede representan todos los números enteros exactamente en el rango -2^53 a + 2^53. (Hacker's Delight, página 262) Si solo usa suma, resta y multiplicación, y mantiene todo en números enteros dentro de este rango, entonces no debería perder precisión. Sin embargo, sería muy cauteloso con la división o las operaciones más complejas.

+0

Si solo vas a usar enteros, ¿por qué no usar un tipo de número entero para empezar? –

+2

Heh - int64_t puede representar todos los enteros exactamente en el rango -2^63 a + 2^63-1. Si solo usa suma, resta y multiplicación, y mantiene todo en números enteros dentro de este rango, entonces no debería perder precisión. Sin embargo, sería muy cauteloso con la división. –

+0

Algunos sistemas anticuados que (¿ay?) Todavía están en uso admiten "doble", pero no admiten ningún tipo de entero de 64 bits. Sugeriría que realizar cálculos como 'doble', escalado que cualquier redondeo requerido semánticamente siempre será para unidades completas, es el enfoque más eficiente. – supercat

0

Sin un doble siempre tendrá errores de redondeo, el uso "decimal" si estás en .Net ...

+6

cuidado. * Cualquier * representación de coma flotante tendrá errores de redondeo, decimal incluido. Es solo que el decimal se redondeará en formas que son intuitivas para los humanos (y generalmente apropiadas para el dinero), y el punto flotante binario no lo hará. Pero para el cruzamiento de números no financieros, el doble a menudo es mucho, mucho mejor que el decimal, incluso en C#. –

2

Usando doble cuando usted no sabe lo que está haciendo no es adecuado.

"doble" puede representar una cantidad de un billón de dólares con un error de 1/90o de un centavo. Entonces obtendrás resultados altamente precisos. ¿Quieres calcular cuánto cuesta poner a un hombre en Marte y recuperarlo vivo? doble hará bien.

Pero con el dinero a menudo hay reglas muy específicas que dicen que un determinado cálculo debe dar un resultado determinado y ningún otro. Si calcula una cantidad muy muy muy próxima a $ 98.135, a menudo existirá una regla que determinará si el resultado debe ser $ 98.14 o $ 98.13 y usted debe seguir esa regla y obtener el resultado que se requiere.

Dependiendo de donde viva, usar enteros de 64 bits para representar centavos o centavos o kopeks o lo que sea que sea la unidad más pequeña en su país generalmente funcionará bien. Por ejemplo, los enteros con signo de 64 bits que representan centavos pueden representar valores de hasta 92,223 billones de dólares. Los enteros de 32 bits generalmente no son adecuados.

-4

En realidad, el punto flotante double es ideal para representar cantidades de dinero siempre que elija una unidad adecuada.

Ver http://www.idinews.com/moneyRep.html

Así es fija puntos largo. O bien consume 8 bytes, seguramente preferible a los 16 consumidos por un elemento decimal.

Si algo funciona o no (es decir, produce el resultado esperado y correcto) no es una cuestión de votación o preferencia individual. Una técnica funciona o no funciona.

+0

Enlazando un artículo que escribió que no concuerda con décadas de prácticas comunes y opciones de expertos que el punto flotante no es adecuado para las representaciones de transacciones financieras, tendrá que tener un poco más de respaldo que una sola página. – MuertoExcobito