Pregunta

Estamos reescribiendo nuestro sistema de contabilidad heredado en VB.NET y SQL Server.Trajimos un nuevo equipo de programadores .NET/SQL para realizar la reescritura.La mayor parte del sistema ya está completo con los montos en dólares usando Floats.El lenguaje del sistema heredado en el que programé no tenía flotante, 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 los montos en dólares?

¿Cuáles son algunos de los pros y los contras de cualquiera de ellos?

Una desventaja mencionada en nuestro scrum diario fue que hay que tener cuidado al calcular una cantidad que arroje un resultado superior a dos posiciones decimales.Parece que tendrás que redondear la cantidad a dos posiciones decimales.

Otra desventaja es que todas las visualizaciones y cantidades impresas deben tener un formato de declaración que muestre dos posiciones decimales.Noté algunas veces que esto no se hacía y las cantidades no parecían correctas.(es decir.10.2 o 10.2546)

Una ventaja es que el flotante solo ocupa 8 bytes en el disco, mientras que el decimal ocuparía 9 bytes (Decimal 12,2).

¿Fue útil?

Solución

¿Se debe utilizar el tipo de datos flotante o decimal para los montos en dólares?

La respuesta es fácil.Nunca flota. NUNCA !

Los flotadores estaban según IEEE 754 siempre binario, 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 enteros positivos), cualquier número decimal como m/(2^n*5^n).
Como los binarios carecen del primo factor 5, todos los números binarios se pueden representar exactamente con 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

Entonces terminas con un número mayor o menor que el número decimal dado.Siempre.

Por que importa ?Redondeo.
El redondeo normal significa 0...4 hacia abajo, 5...9 hacia arriba.Por lo que hace importa si el resultado es 0.049999999999....o 0.0500000000...Quizás sepas que significa 5 centavos, pero la computadora no lo sabe y redondea 0.4999...abajo (mal) y 0.5000...arriba (derecha).
Dado que el resultado de los cálculos en coma flotante siempre contiene pequeños términos de error, la decisión es pura suerte.Es inútil si desea un manejo decimal de redondeo a par con números binarios.

¿No estás convencido?¿Insiste en que en su sistema de cuentas todo está perfectamente bien?
¿Activos y pasivos iguales?Bien, entonces toma cada uno de los números formateados dados de cada entrada, analízalos y súmalos con un sistema decimal independiente.Compare eso con la suma formateada.
Vaya, algo anda mal, ¿no?

Para ese cálculo, se requirió una precisión extrema y fidelidad (usamos el flotador de Oracle) para poder registrar que el "mil millones de un centavo" se aceleró.

No ayuda contra este error.Como todo el mundo supone automáticamente que el ordenador suma correctamente, prácticamente nadie lo comprueba de forma independiente.

Otros consejos

Primero deberías leer esto. Lo que todo informático debería saber sobre la aritmética de coma flotante.Entonces deberías considerar usar algún tipo de punto fijo / número de precisión arbitraria paquete (por ej.java BigNum, módulo decimal de Python) de lo contrario, te encontrarás con un mundo de dolor.Luego, averigüe si usar el tipo decimal SQL nativo es suficiente.

Existen flotantes/dobles (editados) para exponer el rápido x87 fp que ahora está prácticamente obsoleto.No los utilice si le importa la precisión de los cálculos y/o no compensa completamente sus limitaciones.

Solo como 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 utiliza el algoritmo Bankers de forma predeterminada y SQL Server utiliza el redondeo algorítmico simétrico.Consulte el artículo de Wikipedia. aquí

¡Pregunta a tus contadores!Te desaprobarán por usar flotador.Como alguien publicó antes, use float SÓLO si no le importa la precisión.Aunque siempre estaría en contra cuando se trata de dinero.

En el software de contabilidad NO es aceptable un flotador.Utilice decimal con 4 puntos decimales.

Los puntos flotantes tienen números irracionales inesperados.

Por ejemplo, no puedes almacenar 1/3 como decimal, sería 0,3333333333...(etcétera)

En realidad, los flotantes se almacenan como un valor binario y una potencia de 2 exponentes.

Entonces 1.5 se almacena como 3 x 2 elevado a -1 (o 3/2)

El uso de estos exponentes de base 2 crea algunos números irracionales impares, por ejemplo:

Convierta 1.1 a flotante y luego vuelva a convertirlo, su resultado será algo 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 un doble puede manejar.

Más sobre este tema en mi blog, pero básicamente, para el almacenamiento, es mejor usar decimales.

En el servidor Microsoft SQL tienes el money tipo de datos: esto suele ser mejor para el almacenamiento financiero.Tiene una precisión de 4 posiciones decimales.

Para los cálculos, el problema es mayor: la inexactitud es una fracción pequeña, pero si se la coloca en una función de potencia, rápidamente se vuelve significativa.

Sin embargo, los decimales no son muy buenos para ningún tipo de matemáticas; por ejemplo, no hay soporte nativo para potencias decimales.

Utilice el servidor SQL decimal tipo.

No utilice dinero o flotar.

el dinero usa 4 decimales, es más rápido que usar decimales 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.

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 con signo.El punto flotante IEEE es más preciso por bit utilizado, pero eso no importa aquí.

Las cifras financieras se basan en siglos de práctica con papel y bolígrafo, con convenciones asociadas.Son razonablemente precisos pero, lo que es más importante, son reproducibles.Dos contadores que trabajan con distintos números y tasas deberían obtener el mismo número.Cualquier margen de discrepancia es lugar de fraude.

Por lo tanto, para los cálculos financieros, la respuesta correcta es la que dé la misma respuesta que un contador público certificado que sea bueno en aritmética.Esto es aritmética decimal, no punto flotante IEEE.

La única razón para usar Float por dinero es si no le importan las respuestas precisas.

Los flotantes no son representaciones exactas; es posible que haya problemas de precisión, por ejemplo, al sumar valores muy grandes y muy pequeños.Es por eso que se recomiendan los tipos decimales para la moneda, aunque el problema de precisión puede ser bastante raro.

Para aclarar, el tipo decimal 12,2 almacenará esos 14 dígitos exactamente, mientras que el flotante no, ya que utiliza una representación binaria internamente.Por ejemplo, 0,01 no se puede representar exactamente mediante 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, yo era responsable de la parte del sistema de "acumulación de intereses".Cada día, mi código calculaba cuánto interés se había acumulado (ganado) sobre el saldo ese día.

Para ese cálculo, se requería extrema precisión y fidelidad (utilizamos FLOAT de Oracle) para poder registrar la "milmillonésima parte de un centavo" que se acumulaba.

Cuando se trataba de "capitalizar" los intereses (es decir,pagando los intereses nuevamente en su cuenta) el monto se redondeó al centavo.El tipo de datos para los saldos de las cuentas era de dos decimales.(De hecho, era más complicado ya que era un sistema multidivisa que podía funcionar con muchos decimales, pero siempre redondeábamos al "centavo" de esa moneda).Sí, había "fracciones" de pérdidas y ganancias, pero cuando las cifras de las computadoras se actualizaban (dinero pagado o ingresado) siempre eran valores monetarios REALES.

Esto satisfizo a los contables, auditores y evaluadores.

Entonces, consulte con sus clientes.Le informarán sus normas y prácticas bancarias/contables.

Incluso mejor que usar decimales es usar simplemente números enteros antiguos (o tal vez algún tipo de bigint).De esta manera siempre tendrá la mayor precisión posible, pero la precisión se puede especificar.Por ejemplo el número 100 puede 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 el 100 a un valor mayor, como:10 ^ n, donde n es el número de decimales.

Otra cosa que debes 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 realizarse a través de procesos almacenados.Esto previene el fraude, no solo los ataques de inyección SQl.Un usuario interno que quiera cometer fraude nunca debería tener la capacidad de cambiar directamente los datos en las tablas de la base de datos.Este es un control interno crítico en su sistema.¿Realmente quiere que algún empleado descontento vaya al backend de su base de datos y comience a escribirle cheques?¿O ocultar que aprobaron un gasto a un proveedor no autorizado cuando no tienen autoridad de aprobación?Sólo dos personas en toda su organización deberían poder acceder directamente a los datos de su base de datos financiera, su administrador de base y su respaldo.Si tiene muchos administradores de bases de datos, sólo dos de ellos deberían tener este acceso.

Menciono esto porque si sus programadores usaron flotación en un sistema de contabilidad, probablemente no estén familiarizados con la idea de controles internos y no los consideraron en su esfuerzo de programación.

Siempre puedes escribir algo como un tipo Money para .Net.

Mira este articulo: Un tipo de dinero para el CLR - En mi opinión, el autor hizo un trabajo excelente.

Había estado usando el tipo de dinero de SQL para almacenar valores monetarios.Recientemente, tuve que trabajar con varios sistemas de pago en línea y noté que algunos de ellos usan números enteros para almacenar valores monetarios.En mis proyectos actuales y nuevos, comencé a usar números enteros y estoy bastante contento con esta solución.

De las 100 fracciones n/100, donde n es un número natural tal que 0 <= n y n < 100, sólo cuatro pueden representarse como números de coma flotante.Eche un vistazo al resultado de este programa en 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;
}

¿Ha considerado utilizar el tipo de datos monetarios para almacenar cantidades en dólares?

Con respecto a la desventaja de que el decimal ocupa un byte más, diría que no me importa.En 1 millón de filas solo usarás 1 MB más y el almacenamiento es muy barato hoy en día.

Hagas lo que hagas, debes tener cuidado con los errores de redondeo.Calcule utilizando un mayor grado de precisión que el que muestra.

Probablemente desee utilizar alguna forma de representación de punto fijo para los valores de moneda.También querrás investigar el redondeo bancario (también conocido como "redondear a la mitad par"). Evita el sesgo que existe en el método habitual de "redondear a la mitad hacia arriba".

Sus contadores querrán controlar cómo redondea.Usar float 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 (dinero, dinero pequeño), que deben usarse en lugar de flotantes o reales.Almacenar el decimal (12,2) eliminará los redondeos, pero también los eliminará durante los pasos intermedios, lo que realmente no es lo que querrá en absoluto en una aplicación financiera.

Utilice siempre decimales.Float le dará valores inexactos debido a problemas de redondeo.

Los números de coma flotante pueden solo representan números que son una suma de múltiplos negativos de la base; para punto flotante binario, por supuesto, son dos.

Sólo hay cuatro fracciones decimales representables precisamente en coma flotante binaria:0, 0,25, 0,5 y 0,75.Todo lo demás es una aproximación, del mismo modo 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 tener una precisión de cierto número de decimales.

Este es un excelente artículo que describe cuando usar flotante y decimal.Float almacena un valor aproximado y decimal almacena un valor exacto.

En resumen, los valores exactos como el dinero deben usar decimales y los valores aproximados como las mediciones científicas deben usar flotante.

Aquí hay un ejemplo interesante que muestra que tanto el flotante como el decimal son capaces de perder precisión.Cuando sumar un número que no es un número entero y luego restar ese mismo número flotante da como resultado una pérdida de 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

Al multiplicar un número no entero y dividir por ese mismo número, los decimales pierden precisión mientras que los flotantes 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top