sql >> Base de Datos >  >> RDS >> Mysql

¿La mejor práctica para almacenar pesos en una base de datos SQL?

Usted afirma que hay inexactitudes inherentes en los números de coma flotante. Creo que esto merece ser explorado un poco primero.

Al decidir sobre un sistema numérico para representar un número (ya sea en una hoja de papel, en un circuito de computadora o en otro lugar), hay dos separados cuestiones a considerar:

  1. su base; y

  2. su formato .

Elige una base, cualquier base...

Limitado por el espacio finito, no se puede representar un miembro arbitrario de un conjunto infinito . Por ejemplo:no importa cuánto papel compre o qué tan pequeña sea su letra, siempre será posible encontrar un número entero que no quepa en el espacio dado (puede seguir agregando dígitos adicionales hasta que se agote el papel). Entonces, con enteros , generalmente restringimos nuestro espacio finito para representar solo aquellos que caen dentro de un intervalo particular, p. si tenemos espacio para el signo positivo/negativo y tres dígitos, podríamos restringirnos al intervalo [-999,+999] .

Cada no vacío intervalo contiene un conjunto infinito de números reales. En otras palabras, no importa qué intervalo uno tome sobre los números reales —ya sea [-999,+999] , [0,1] , [0.000001,0.000002] o cualquier otra cosa:¡todavía hay un conjunto infinito de reales dentro de ese intervalo (uno solo necesita seguir agregando dígitos fraccionarios (distintos de cero))! Por lo tanto, los números reales arbitrarios deben siempre ser "redondeado" a algo que puede representarse en un espacio finito.

El conjunto de números reales que se pueden representar en un espacio finito depende del sistema numérico que se utilice. En nuestro (familiar) posicional base-10 sistema, el espacio finito será suficiente para la mitad (0.510 ) pero no para un tercio (0.33333…10 ); por el contrario, en el (menos familiar) posicional base-9 system, es al revés (esos mismos números son respectivamente 0.44444…9 y 0.39 ). La consecuencia de todo esto es que algunos números que se pueden representar usando solo una pequeña cantidad de espacio en base 10 posicional (y por lo tanto aparecen ser muy "redondo" para nosotros los humanos), p. una décima, en realidad requeriría infinitos circuitos binarios para ser almacenados con precisión (y por lo tanto no parecen ser muy "redondos" para nuestros amigos digitales)! En particular, dado que 2 es un factor de 10, no ocurre lo mismo a la inversa:cualquier número que se pueda representar con binario finito también se puede representar con decimal finito.

No podemos hacerlo mejor para cantidades continuas. En última instancia, tales cantidades deben usar una representación finita en algunos sistema numérico:es arbitrario si ese sistema resulta ser fácil en circuitos de computadora, en dedos humanos, en otra cosa o en nada en absoluto; cualquiera que sea el sistema que se use, el valor debe ser redondeado y por lo tanto siempre da como resultado un "error de representación".

En otras palabras, incluso si uno tiene un instrumento de medición perfectamente preciso (lo cual es físicamente imposible), entonces cualquier medición que informe ya habrá sido redondeada a un número que se ajusta a su pantalla (en cualquier base que use, generalmente decimal, por razones obvias). Por lo tanto, "86,2 oz" nunca es realmente "86,2 oz " sino más bien una representación de "algo entre 86,1500000... oz y 86,2499999... oz ". (En realidad, debido a que en realidad el instrumento es imperfecto, todo lo que podemos decir es que tenemos algunos grado de confianza que el valor real cae dentro de ese intervalo, pero eso definitivamente se está desviando del punto aquí).

Pero podemos hacerlo mejor para cantidades discretas . Dichos valores no son "números reales arbitrarios" y, por lo tanto, nada de lo anterior se aplica a ellos:se pueden representar exactamente en el sistema numérico en el que se definieron y, de hecho, deberían ser (ya que convertir a otro sistema numérico y truncar a una longitud finita daría como resultado el redondeo a un número inexacto). Las computadoras pueden (de manera ineficiente) manejar tales situaciones al representar el número como una cadena:p. considere ASCII o BCD codificación.

Aplicar un formato...

Dado que es una propiedad de la base (algo arbitraria) del sistema numérico, si un valor parece ser "redondo" o no, no influye en su precisión. . Esa es una observación realmente importante , que va en contra de la intuición de muchas personas (y es la razón por la que pasé tanto tiempo explicando la base numérica arriba).

En cambio, la precisión está determinada por cuántas cifras significativas una representación tiene . Necesitamos un formato de almacenamiento que sea capaz de registrar nuestros valores en al menos tantas cifras significativas como las consideremos correctas . Tomando a modo de ejemplo valores que consideramos correctos cuando se expresan como 86.2 y 0.0000862 , las dos opciones más comunes son:

  • Punto fijo , donde el número de cifras significativas depende de la magnitud :p.ej. en una representación fija de 5 puntos decimales, nuestros valores se almacenarían como 86.20000 y 0.00009 (y por lo tanto tienen 7 y 1 cifras significativas de precisión respectivamente). En este ejemplo, se ha perdido precisión en el último valor (y, de hecho, no nos costaría mucho más haber sido totalmente incapaces de representar nada de importancia); y el valor anterior almacenado falsa precisión , que es un desperdicio de nuestro espacio finito (y, de hecho, no haría falta mucho más para que el valor sea tan grande que desborde la capacidad de almacenamiento).

    Un ejemplo común de cuándo este formato podría ser apropiado es para un sistema de contabilidad:las sumas monetarias generalmente deben rastrearse hasta el centavo independientemente de su magnitud (por lo tanto, se requiere menos precisión para valores pequeños y más precisión para valores grandes). Da la casualidad de que la moneda también suele considerarse discreta (los centavos son indivisibles), por lo que este también es un buen ejemplo de una situación en la que es deseable una base particular (decimal para la mayoría de las monedas modernas) para evitar los errores de representación discutidos anteriormente.

  • Coma flotante , donde el número de cifras significativas es constante independientemente de la magnitud :p.ej. en representación decimal de 5 cifras significativas, nuestros valores se almacenarían como 86.200 y 0.000086200 (y, por definición, tienen 5 cifras significativas de precisión en ambas ocasiones). En este ejemplo, ambos valores se han almacenado sin ninguna pérdida de precisión; y ambos también tienen la misma cantidad de precisión falsa, que es menos derrochador (y, por lo tanto, podemos usar nuestro espacio finito para representar un rango mucho mayor de valores, tanto grandes como pequeños).

    Un ejemplo común de cuándo este formato podría ser apropiado es para registrar cualquier medida del mundo real :la precisión de los instrumentos de medición (que adolecen tanto de sistemático y aleatorio errores) es bastante constante independientemente de la escala, por lo que, dadas suficientes cifras significativas (generalmente alrededor de 3 o 4 dígitos), no se pierde absolutamente ninguna precisión incluso si un cambio de base resultó en el redondeo a un número diferente .

    Pero qué tan precisos son los formatos de almacenamiento de coma flotante utilizado por nuestras computadoras?

    • Un IEEE754 punto flotante de precisión simple (binary32) número tiene 24 bits, o log10(2) (más de 7) dígitos, de importancia, es decir, tiene una tolerancia de menos de ±0.000006% . En otras palabras, es más preciso que decir "86.20000 ".

    • Un IEEE754 coma flotante de doble precisión (binary64) número tiene 53 bits, o log10(2) (casi 16) dígitos, de importancia, es decir, tiene una tolerancia de poco más de ±0.00000000000001% . En otras palabras, es más preciso que decir "86.2000000000000 ".

    Lo más importante a tener en cuenta es que estos formatos son, respectivamente, más de diez mil y más de un billón veces más precisa que decir "86,2", aunque las conversiones exactas del binario a decimal incluyen una precisión falsa errónea (que debemos ignorar:¡hablaremos más sobre esto en breve)!

Observe también que ambos fija y los formatos de punto flotante darán como resultado una pérdida de precisión cuando un valor se conoce con más precisión de lo que admite el formato. Tales errores de redondeo puede propagarse en operaciones aritméticas para producir resultados aparentemente erróneos (lo que sin duda explica su referencia a las "inexactitudes inherentes" de los números de punto flotante):por ejemplo, 3 × 3000 en punto fijo de 5 posiciones produciría 999.99000 en lugar de 1000.00000; y 7 − ⁄50 en punto flotante de 5 cifras significativas daría 0.0028600 en lugar de 0.0028571 .

El campo del análisis numérico se dedica a comprender estos efectos, pero es importante darse cuenta de que cualquier Un sistema utilizable (incluso realizando cálculos en su cabeza) es vulnerable a tales problemas porque ningún método de cálculo que garantice la finalización puede ofrecer una precisión infinita :considere, por ejemplo, cómo calcular el área de un círculo; necesariamente habrá una pérdida de precisión en el valor utilizado para π, que se propagará al resultado.

Conclusión

  1. Las medidas del mundo real deben usar punto flotante binario :es rápido, compacto, extremadamente preciso y no es peor que cualquier otra cosa (incluida la versión decimal desde la que comenzó). Desde tipos de datos de punto flotante de MySQL son IEEE754, esto es exactamente lo que ofrecen.

  2. Las aplicaciones de divisas deben usar denario de punto fijo :si bien es lento y desperdicia memoria, garantiza que los valores no se redondeen a cantidades inexactas y que no se pierdan centavos en grandes sumas monetarias. Dado que tipos de datos de punto fijo de MySQL son cadenas codificadas en BCD, esto es exactamente lo que ofrecen.

Por último, tenga en cuenta que los lenguajes de programación suelen representar valores fraccionarios utilizando punto flotante binario tipos:por lo tanto, si su base de datos almacena valores en otro formato, debe tener cuidado de cómo se introducen en su aplicación o, de lo contrario, pueden convertirse (con todos los problemas que ello conlleva) en la interfaz.

¿Qué opción es mejor en este caso?

Espero haberte convencido de que tus valores pueden (y deberían) ) almacenarse en tipos de coma flotante sin preocuparse demasiado por las "inexactitudes"? Recuerda, son más precisa de lo que alguna vez fue su endeble representación decimal de 3 dígitos significativos:solo tiene que ignorar la precisión falsa (pero uno debe siempre hazlo de todos modos, incluso si usas un formato decimal de punto fijo).

En cuanto a su pregunta:elija la opción 1 o 2 en lugar de la opción 3; facilita las comparaciones (por ejemplo, para encontrar la masa máxima, uno podría usar MAX(mass) , mientras que hacerlo de manera eficiente en dos columnas requeriría algo de anidamiento).

Entre esos dos, no importa cuál elija:los números de punto flotante se almacenan con un número constante de bits significativos independientemente de su escala .

Además, mientras que en el caso general podría suceder que algunos valores se redondeen a números binarios más cercanos a su representación decimal original usando la opción 1, mientras que simultáneamente otros se redondeen a números binarios más cercanos a su representación decimal original usando la opción 2, como pronto veremos que tales errores de representación solo se manifiestan dentro de la falsa precisión que siempre debe ignorarse.

Sin embargo, en este caso, dado que sucede que hay 16 onzas en 1 libra (y 16 es una potencia de 2), las diferencias relativas entre los valores decimales originales y los números binarios almacenados usando los dos enfoques son idénticas :

  1. 5.387510 (no 5.3367187510 como se indica en su pregunta) se almacenaría en un float binary32 como 101.0110001100110011001102 (que es 5.3874998092651367187510 ):esto es 0.0000036% del valor original (pero, como se discutió anteriormente, el "valor original" ya era una representación bastante mala de la cantidad física que representa).

    Sabiendo que un float binary32 almacena solo 7 dígitos decimales de precisión, nuestro compilador sabe con certeza que todo, desde el 8º dígito en adelante, es definitivamente precisión falsa y por lo tanto debe ser ignorado en cada caso, por lo tanto, siempre que nuestro valor de entrada no requiera más precisión que eso (y si lo hizo, binary32 fue obviamente la elección incorrecta de formato), esto garantiza un retorno a un valor decimal que parece tan redondo como el que empezamos:5.38750010 . Sin embargo, realmente deberíamos aplicar conocimiento del dominio en este punto (como deberíamos hacer con cualquier formato de almacenamiento) para descartar cualquier otra precisión falsa que pueda existir, como esos dos ceros finales.

  2. 86.210 se almacenaría en un float binary32 como 1010110.001100110011001102 (que es 86.199996948242187510 ):esto también es 0.0000036% del valor original. Como antes, ignoramos la precisión falsa para volver a nuestra entrada original.

Observe cómo las representaciones binarias de los números son idénticas, excepto por la ubicación del punto radix (que está a cuatro bits de distancia):

101.0110 00110011001100110
101 0110.00110011001100110

Esto se debe a que 5,3875 × 2 =86,2.