En Firebird tenemos dos tipos de datos numéricos que tienen una cantidad fija de lugares decimales: NUMERIC y DECIMAL. También tenemos dos tipos de datos numéricos cuya cantidad de lugares decimales es variable (en Matemática se les dice «flotantes») que son: FLOAT y DOUBLE PRECISION.

En este artículo nos referiremos a NUMERIC y a DECIMAL, los cuales siempre tienen una cantidad fija de lugares decimales.

A la cantidad total de dígitos se la llama precisión y se la representa con la letra «p«. A la cantidad de lugares decimales se le llama escala (scale, en inglés) y se la representa con la letra «s«.

Por lo tanto, si una columna está definida como NUMERIC(5, 2) se la representa así: ppp.ss

La precisión debe estar entre 1 y 18. La escala debe estar entre 0 y 18. La escala siempre debe ser menor o igual que la precisión, nunca puede ser mayor.

Si quieres, puedes declarar una columna NUMERIC o DECIMAL sin especificar la precisión y en ese caso será convertida a INTEGER.

Lo importante a recordar es que en NUMERIC y en DECIMAL la parte decimal es siempre exacta. Se necesita que la parte decimal sea exacta cuando se trata de dinero o de cualquier otro número que resulta de contar o de realizar operaciones aritméticas sobre unidades.

Como el resultado de multiplicar o dividir números que tienen una parte decimal fija es predecible, es el motivo por el cual se los utiliza cuando se trata de dinero. A tu usuario no le puedes decir: «el cliente Juan Pueblo está debiendo más o menos 100 dólares», el usuario quiere conocer la cantidad exacta de la deuda de Juan Pueblo, eso de «más o menos 100 dólares» no le va a gustar.

Como tanto NUMERIC como DECIMAL usan una escala se los suele llamar «escalados«. Otros sinónimos son: «números de punto fijo» o «números de coma fija«.

En el estandar SQL-92 tanto NUMERIC como DECIMAL restringen al número almacenado a estar dentro de la escala elegida. La diferencia entre ambos tipos de datos es en la forma en la cual la precisión es restringida. Cuando el tipo de dato es NUMERIC, la precisión es exactamente la declarada. En cambio, cuando el tipo de datos es DECIMAL la precisión es al menos igual a la declarada. Eso significa que NUMERIC es más estricto que DECIMAL.

Sin embargo, en Firebird NUMERIC y DECIMAL son idénticos excepto cuando la precisión es menor que 5. Ambos cumplen con el estandar SQL-92 para el tipo de datos DECIMAL y eso significa que NUMERIC no cumple con el estandar de SQL-92.

Si además de usar Firebird usas o has usado o piensas usar otro motor SQL entonces para el dinero utiliza NUMERIC porque es el recomendado según el estandar SQL-92. Si solamente usas y piensas usar Firebird, entonces puedes usar cualquiera de los dos.

Internamente, Firebird almacena a estos tipos de datos como SMALLINT, como INTEGER, o como BIGINT, dependiendo de la precisión declarada.

La forma de almacenarlos internamente es la siguiente:

  • Se almacena un número entero, o sea que no tiene decimales
  • Se almacena la escala a un factor menor que cero que representa a un exponente de 10

Cuando el número debe ser utilizado en una consulta o en una operación matemática se lo obtiene multiplicando el número entero almacenado por 10 elevado a la escala almacenada.

Ejemplo:

Un número ha sido declarado como NUMERIC(4, 3) y por lo tanto Firebird lo almacena como un SMALLINT.

Quieres guardar en esa columna el número 7,2348 pero como la escala de la columna es 3 y la escala del número que quieres guardar es 4 el Firebird lo redondea a 7,235

Por lo tanto, el Firebird guarda el número 7235 y el número -3

Cuando quieres utilizar ese número el Firebird multiplica 7235 por 10 elevado a la -3 y por lo tanto obtienes 7,235 ya que multiplicar un número por 10 elevado a la -3 es lo mismo que dividirlo por 1000. Y 7235 dividido por 1000 es igual a 7,235

Rango de valores

Tipo de datos                Precisión    Almacenado como

NUMERIC             1 a 4    SMALLINT
DECIMAL             1 a 4    INTEGER
NUMERIC y DECIMAL   5 a 9    INTEGER
NUMERIC y DECIMAL  10 a 18   BIGINT

El tipo de datos NUMERIC

El formato del tipo de datos NUMERIC es:

NUMERIC(p, s)

Por ejemplo, NUMERIC(4, 2) define a un número que puede tener como máximo 4 dígitos, de los cuales 2 de ellos sí o sí corresponderán a la parte decimal.

Eso significa que el número 1,23 será almacenado como 1,23 pero el número 1,234 será almacenado como 1,23 porque la escala es 2 y por lo tanto no puede almacenarse con más de 2 dígitos decimales. También 1,2345678 será almacenado como 1,23. El número 567,89 no podrá ser almacenado porque la parte entera tiene 3 dígitos y solamente debería tener 2 dígitos, si intentamos almacenarlo el Firebird nos mostrará un error de overflow (número mayor que el máximo permitido para esa columna).

Sin embargo, como NUMERIC se guarda internamente como un SMALLINT y en SMALLINT se puede guardar desde el número -32.768 hasta el número 32.767 al declarar a una columna como NUMERIC(4, 2) el mayor número que podemos almacenar en ella no es 99,99 como podría suponerse viendo la declaración sino 327,67. Cuidado con esto porque NUMERIC no cumple el estándar SQL-92 ya que según ese estándar el mayor número tendría que ser 99,99.

El tipo de datos DECIMAL

El formato del tipo de datos DECIMAL es:

DECIMAL(p, s)

Similarmente a NUMERIC el mayor número que puede guardarse en una columna de este tipo no es el declarado. Por ejemplo si una columna se declara como DECIMAL(4, 1) uno podría pensar que el máximo número permitido sería 999,9 pero sin embargo el mayor número permitido es 214.748.364,7 y el menor número permitido es -214.748.364,8

¿Por qué eso? porque el tipo de datos DECIMAL se guarda internamente como un INTEGER y el rango de valores de INTEGER es de -2.147.483.648 hasta 2.147.483.647 y como en este ejemplo la escala es 1 entonces para hallar el rango de valores se divide por 10. Si la escala fuera 2 se dividiría por 100, si fuera 3 se dividiría por 1.000 y así sucesivamente.

Advertencia

El hecho de que puedas almacenar en una columna números mayores a los declarados para esa columna no significa que debas hacerlo. En futuras versiones de Firebird este comportamiento puede cambiar para adecuarse al estándar y si estás dependiendo de algo que está fuera del estándar podrás encontrarte con graves problemas más adelante.

Comportamiento de NUMERIC y DECIMAL en operaciones aritméticas

División

La precisión del resultado siempre es 18. La escala del resultado es la suma de las escalas del dividendo y del divisor. Esto implica que la escala del cociente siempre será mayor que las del dividendo y del divisor. Debes tener cuidado con esto si los números que empleas tienen muchos decimales o podrías sobrepasar la escala máxima que es 18 o necesitar una precisión mayor que 18 y en ambos casos eso te ocasionaría un error de overflow.

SELECT
   1234567.89 / 54321.2345
FROM
   RDB$DATABASE

El resultado de esa división es 22,727169 que como puedes ver tiene 6 dígitos decimales. ¿Por qué? porque el dividendo tiene 2 dígitos decimales y el divisor tiene 4 dígitos decimales y si sumas 2 + 4 obtienes 6.

SELECT
   1 / 3
FROM
   RDB$DATABASE

Aquí, uno esperaría que el resultado fuera 0,33333333 sin embargo el resultado es 0 (cero). ¿Por qué? porque tanto el dividendo como el divisor son números enteros y la escala de los números enteros es cero. Y como la escala del cociente es la suma de la escala del dividendo más la escala del divisor esa escala también debe ser cero (0 + 0 = 0).

Si queremos efectuar la división de arriba pero que el resultado tenga dos decimales entonces deberíamos escribir:

SELECT
   1.00 / 3
FROM
   RDB$DATABASE

o también podríamos escribir:

SELECT
   (1 + 0.00) / 3
FROM
   RDB$DATABASE

Multiplicación

Al igual que en la división, la escala del resultado es la suma de las escalas de los operandos.

Suma y Resta

La escala del resultado es la mayor de las dos escalas.

SELECT
   123.45 + 678.9
FROM
   RDB$DATABASE

El resultado de esta suma es 802,35 y como puedes ver la escala es 2. ¿Por qué la escala es 2? porque las escalas de los sumandos son 2 y 1. Como 2 es mayor que 1 entonces la escala del resultado es 2.

La precisión siempre es 18.

Artículos relacionados

Entendiendo NUMERIC y DECIMAL

Confusiones comunes al declarar una columna como NUMERIC o DECIMAL

Determinando la precisión y la escala de una columna NUMERIC

Eligiendo el tipo de datos numérico que se usará en una columna

El índice del blog Firebird21

El foro del blog Firebird21