Estamos reescribiendo nuestro sistema de contabilidad heredado en VB.NET y SQL Server. Trajimos un nuevo equipo de programadores .NET/SQL para hacer la reescritura. La mayor parte del sistema ya se ha completado con las cantidades en dólares utilizando Flotadores. El lenguaje del sistema heredado, en el que programé, no tenía un flotador, por lo que probablemente habría usado un decimal.
¿Cuál es tu recomendación?
¿Se debe utilizar el tipo de datos Flotante o Decimal para montos en dólares?
¿Cuáles son algunas de las ventajas y desventajas de cualquiera de los dos?
Una de las Con que mencionamos en nuestro scrum diario fue que debes tener cuidado al calcular una cantidad que devuelve un resultado que supera las dos posiciones decimales. Parece que tendrá que redondear la cantidad a dos posiciones decimales.
Otra Con es todas las pantallas y las cantidades impresas deben tener una Declaración de Formato que muestre dos posiciones decimales. Noté varias veces que esto no se hizo y las cantidades no se veían correctas. (es decir, 10.2 o 10.2546)
Un pro es el Float solo ocupa 8 bytes en el disco donde el Decimal tomaría 9 bytes (12,2 Decimal)
¿Se debe utilizar el tipo de datos Flotante o Decimal para montos en dólares?
La respuesta es fácil. Nunca flota. NUNCA!
Los flotadores fueron de acuerdo con IEEE 754 siempre binarios, solo el nuevo estándar IEEE 754R formatos decimales definidos. Muchas de las partes binarias fraccionarias nunca pueden igualar la representación decimal exacta.
Cualquier número binario se puede escribir como m/2^n
(m
, n
integer positivo), cualquier número decimal como m/(2^n*5^n)
.
Como los binarios carecen del factor 5
principal, todos los números binarios pueden estar representados exactamente por decimales, pero no al revés.
0.3 = 3/(2^1 * 5^1) = 0.3
0.3 = [0.25/0.5] [0.25/0.375] [0.25/3.125] [0.2825/3.125]
1/4 1/8 1/16 1/32
Así que terminas con un número más alto o más bajo que el número decimal dado. Siempre.
Por que importa ? Redondeo.
Redondeo normal significa 0..4 abajo, 5..9 arriba. Así que no importa si el resultado es 0.049999999999
.... o 0.0500000000
... Es posible que sepa que significa 5 centavos, pero la computadora no sabe eso y redondea 0.4999
... down (incorrecto) y 0.5000
... up (derecha).
Dado que el resultado de los cálculos de punto flotante siempre contienen pequeños términos de error, la decisión es pura suerte. Se vuelve irremediable si desea un manejo de redondeo a par decimal con números binarios.
¿No está convencido? ¿Insiste en que en su sistema de cuentas todo está perfectamente bien?
¿Activos y pasivos iguales? De acuerdo, tome cada uno de los números con formato de cada entrada, analícelos y sumelos con un sistema decimal independiente. Compare eso con la suma formateada.
Vaya, hay algo mal, ¿no es así?
Para ese cálculo, se requirió una precisión y fidelidad extremas (usamos el FLOAT de Oracle) para poder registrar la "billonésima parte de un centavo" que fue acusado.
No ayuda contra este error. Debido a que todas las personas asumen automáticamente que la computadora suma correctamente, prácticamente nadie verifica de forma independiente.
Esta foto responde:
Esta es otra situación: el hombre de Northampton recibió una carta en la que se le confiscaba que su hogar sería secuestrado si no pagaba cero dólares y cero centavos!
Primero debes leer esto Lo que todo científico informático debe saber sobre la aritmética de punto flotante . Entonces realmente deberías considerar usar algún tipo de número de punto fijo/precisión arbitraria paquete (por ejemplo, Java BigNum, módulo decimal de Python) de lo contrario, estarás en un mundo de daño. Luego, averigüe si usar el tipo decimal nativo de SQL es suficiente.
Floats/doubles existe (ed) para exponer el fp rápido de x87 que ahora es bastante obsoleto. No los use si le importa la precisión de los cálculos y/o no compensa completamente sus limitaciones.
Al igual que una advertencia adicional, SQL Server y .Net framework utilizan un algoritmo predeterminado diferente para el redondeo. Asegúrese de revisar el parámetro MidPointRounding en Math.Round (). .Net Framework usa el algoritmo de Bankers por defecto y SQL Server usa el redondeo algorítmico simétrico Revisa el artículo de Wikipedia aquí
Pregunte a sus contadores! Te fruncirán el ceño por usar flotador. Como alguien publicado anteriormente, use float SOLAMENTE si no le importa la precisión. Aunque siempre estaría en contra cuando se trata de dinero.
En el software contable NO es aceptable un flotador. Usa decimal con 4 puntos decimales.
Los puntos flotantes tienen números irracionales inesperados.
Por ejemplo, no puede almacenar 1/3 como decimal, sería 0.3333333333 ... (y así sucesivamente)
Los flotadores se almacenan realmente como un valor binario y una potencia de 2 exponentes.
Entonces 1.5 se almacena como 3 x 2 al -1 (o 3/2)
El uso de estos exponentes base-2 crea algunos números irracionales impares, por ejemplo:
Convierta 1.1 en un flotador y luego vuelva a convertirlo, su resultado será algo así como: 1.0999999999989
Esto se debe a que la representación binaria de 1.1 es en realidad 154811237190861 x 2 ^ -47, más de lo que puede manejar un doble.
Más información sobre este tema en mi blog , pero básicamente, para el almacenamiento, es mejor con decimales.
En el servidor Microsoft SQL, tiene el tipo de datos money
- este suele ser el mejor para el almacenamiento financiero. Tiene una precisión de 4 posiciones decimales.
Para los cálculos, tiene más problemas: la inexactitud es una pequeña fracción, pero póngala en una función de poder y rápidamente se vuelve significativa.
Sin embargo, los decimales no son muy buenos para cualquier tipo de matemática; por ejemplo, no hay soporte nativo para los poderes decimales.
Un poco de historia aquí ...
Ningún sistema numérico puede manejar todos los números reales con precisión. Todos tienen sus limitaciones, y esto incluye tanto el punto flotante estándar IEEE como el decimal firmado. El punto flotante IEEE es más preciso por bit utilizado, pero eso no importa aquí.
Los números financieros se basan en siglos de práctica de papel y lápiz, con convenciones asociadas. Son razonablemente precisos, pero, lo que es más importante, son reproducibles. Dos contadores que trabajan con varios números y tasas deben tener el mismo número. Cualquier espacio para la discrepancia es espacio para el fraude.
Por lo tanto, para los cálculos financieros, la respuesta correcta es lo que da la misma respuesta que un CPA que es bueno en aritmética. Esto es aritmética decimal, no punto flotante IEEE.
Utilice el tipo decimal del servidor SQL.
No utilice dinero o flotante .
el dinero usa 4 lugares decimales, es más rápido que usar el decimal PERO sufre de algunos problemas obvios y otros no tan obvios con el redondeo ( ver este problema de conexión )
Lo que recomendaría es usar enteros de 64 bits que almacenen todo en centavos.
Los flotadores no son representaciones exactas, los problemas de precisión son posibles, por ejemplo, al agregar valores muy grandes y muy pequeños. Es por eso que los tipos decimales se recomiendan para la moneda, aunque el problema de precisión sea lo suficientemente raro.
Para aclarar, el tipo decimal 12,2 almacenará esos 14 dígitos exactamente, mientras que el flotante no lo hará, ya que utiliza una representación binaria internamente. Por ejemplo, 0.01 no puede representarse exactamente por un número de punto flotante; la representación más cercana es en realidad 0.0099999998
Para un sistema bancario que ayudé a desarrollar, fui responsable de la parte de "acumulación de intereses" del sistema. Cada día, mi código calculó cuánto interés se había acumulado (ganado) en el saldo de ese día.
Para ese cálculo, se requirió una precisión y fidelidad extremas (usamos FLOAT de Oracle) para poder registrar la "mil millonésima parte de un centavo" que se estaba acumulando.
Cuando se trató de "capitalizar" los intereses (es decir, pagar los intereses en su cuenta), la cantidad se redondeaba al centavo. El tipo de datos para los saldos de cuenta fue dos decimales. (De hecho, era más complicado ya que era un sistema de múltiples monedas que podía funcionar en muchos lugares decimales, pero siempre nos redondeamos al "centavo" de esa moneda). Sí, allí donde había "fracciones" de pérdidas y ganancias, pero cuando se actualizaron las cifras de las computadoras (dinero pagado o pagado), siempre se obtuvieron los valores de dinero REAL.
Esto satisfizo a los contadores, auditores y evaluadores.
Por lo tanto, consulte con sus clientes. Le informarán sus prácticas y normas bancarias/contables.
La única razón para usar Float por dinero es si no le importan las respuestas precisas.
Otra cosa que debe tener en cuenta en los sistemas contables es que nadie debe tener acceso directo a las tablas. Esto significa que todo acceso al sistema de contabilidad debe ser a través de procesos almacenados. Esto es para prevenir el fraude, no solo los ataques de inyección SQl. Un usuario interno que quiera cometer fraude no debería tener la capacidad de cambiar directamente los datos en las tablas de la base de datos, nunca. Este es un control interno crítico en su sistema. ¿De verdad quieres que algún empleado descontento vaya al backend de tu base de datos y que comience a escribir sus cheques? ¿O puede ocultar que aprobaron un gasto a un proveedor no autorizado cuando no tienen autoridad de aprobación? Solo dos personas en toda su organización deberían poder acceder directamente a los datos en su base de datos financiera, su dba y su respaldo. Si tiene muchos dbas, solo dos de ellos deberían tener este acceso.
Menciono esto porque si sus programadores usaron flotar en un sistema de contabilidad, probablemente no estén familiarizados con la idea de los controles internos y no los consideraron en su esfuerzo de programación.
Incluso mejor que usar decimales es usar solo números enteros viejos (o tal vez algún tipo de bigint). De esta manera, siempre tendrá la mayor precisión posible, pero se puede especificar la precisión. Por ejemplo, el número 100
podría significar 1.00
, que tiene el formato siguiente:
int cents = num % 100;
int dollars = (num - cents) / 100;
printf("%d.%02d", dollars, cents);
Si desea tener más precisión, puede cambiar los 100 a un valor mayor, como: 10 ^ n, donde n es el número de decimales.
De las 100 fracciones n/100, donde n es un número natural tal que 0 <= n y n <100, solo cuatro pueden representarse como números de punto flotante. Echa un vistazo a la salida de este programa C:
#include <stdio.h>
int main()
{
printf("Mapping 100 numbers between 0 and 1 ");
printf("to their hexadecimal exponential form (HEF).\n");
printf("Most of them do not equal their HEFs. That means ");
printf("that their representations as floats ");
printf("differ from their actual values.\n");
double f = 0.01;
int i;
for (i = 0; i < 100; i++) {
printf("%1.2f -> %a\n",f*i,f*i);
}
printf("Printing 128 'float-compatible' numbers ");
printf("together with their HEFs for comparison.\n");
f = 0x1p-7; // ==0.0071825
for (i = 0; i < 0x80; i++) {
printf("%1.7f -> %a\n",f*i,f*i);
}
return 0;
}
Siempre puedes escribir algo como un tipo de dinero para .Net.
Eche un vistazo a este artículo: n tipo de dinero para el CLR - El autor hizo un excelente trabajo en mi opinión.
Había estado usando el tipo de dinero de SQL para almacenar valores monetarios. Recientemente, he tenido que trabajar con varios sistemas de pago en línea y he notado que algunos de ellos utilizan números enteros para almacenar valores monetarios. En mis proyectos actuales y nuevos, comencé a usar enteros y estoy muy contento con esta solución.
Es probable que desee utilizar alguna forma de representación de punto fijo para los valores de moneda. También querrá investigar el redondeo de la Banca (también conocido como "mitad redonda"). Evita el sesgo que existe en el método habitual de "redondear el medio arriba".
¿Ha considerado utilizar el tipo de datos de dinero para almacenar montos en dólares?
Con respecto a la Con, ese decimal ocupa un byte más, diría que no me importa. En 1 millón de filas solo usará 1 MB más y el almacenamiento es muy barato en estos días.
Hagas lo que hagas, debes tener cuidado con los errores de redondeo. Calcule usando un mayor grado de precisión del que muestra.
Tus contadores querrán controlar cómo redondeas. Usar flotante significa que estará redondeando constantemente, generalmente con una declaración de tipo FORMAT (), que no es la forma en que desea hacerlo (use piso/techo en su lugar).
Tiene tipos de datos de moneda (money, smallmoney), que se deben utilizar en lugar de float o real. El almacenamiento de decimal (12,2) eliminará sus redondeos, pero también los eliminará durante los pasos intermedios, lo que realmente no es lo que deseará en una aplicación financiera.
Este es un excelente artículo que describe cuándo usar flotante y decimal . Float almacena un valor aproximado y decimal almacena un valor exacto.
En resumen, los valores exactos como el dinero deberían usar el decimal, y los valores aproximados como las mediciones científicas deberían usar el flotador.
Aquí hay un ejemplo interesante que muestra que tanto la coma flotante como la decimal son capaces de perder precisión. Al agregar un número que no es un número entero y luego restar el mismo número, los resultados en la flotación pierden precisión, mientras que el decimal no:
DECLARE @Float1 float, @Float2 float, @Float3 float, @Float4 float;
SET @Float1 = 54;
SET @Float2 = 3.1;
SET @Float3 = 0 + @Float1 + @Float2;
SELECT @Float3 - @Float1 - @Float2 AS "Should be 0";
Should be 0
----------------------
1.13797860024079E-15
Cuando se multiplica un número no entero y se divide por el mismo número, los decimales pierden precisión mientras que los flotadores no.
DECLARE @Fixed1 decimal(8,4), @Fixed2 decimal(8,4), @Fixed3 decimal(8,4);
SET @Fixed1 = 54;
SET @Fixed2 = 0.03;
SET @Fixed3 = 1 * @Fixed1 / @Fixed2;
SELECT @Fixed3 / @Fixed1 * @Fixed2 AS "Should be 1";
Should be 1
---------------------------------------
0.99999999999999900
Siempre use decimal. Float le dará valores inexactos debido a problemas de redondeo.
Los números de punto flotante pueden solo representar números que son una suma de múltiplos negativos de la base; para el punto flotante binario, por supuesto, son dos.
Solo hay cuatro fracciones decimales que se pueden representar precisamente en punto flotante binario: 0, 0.25, 0.5 y 0.75. Todo lo demás es una aproximación, de la misma manera que 0.3333 ... es una aproximación de 1/3 en aritmética decimal.
El punto flotante es una buena opción para cálculos donde lo importante es la escala del resultado. Es una mala elección cuando intentas ser exacto con un número de decimales.