Différence entre le type de données flottant et décimal

175

Quelle différence cela fait-il lorsque j'utilise des types de données float et decimal dans MySQL?.

Quand devrais-je utiliser lequel?

Pirate
la source
Ne pas utiliser FLOAT(m,n), cela conduit à deux arrondis; en attendant, il n'apporte aucune utilité.
Rick James

Réponses:

185

C'est ce que j'ai trouvé quand j'ai eu ce doute.

mysql> create table numbers (a decimal(10,2), b float);
mysql> insert into numbers values (100, 100);
mysql> select @a := (a/3), @b := (b/3), @a * 3, @b * 3 from numbers \G
*************************** 1. row ***************************
  @a := (a/3): 33.333333333
  @b := (b/3): 33.333333333333
@a + @a + @a: 99.999999999000000000000000000000
@b + @b + @b: 100

La décimale a fait exactement ce qui est censé faire sur ces cas, elle a tronqué le reste, perdant ainsi la partie 1/3.

Donc, pour les sommes, la décimale est meilleure, mais pour les divisions, le flottant est meilleur, jusqu'à un certain point, bien sûr. Je veux dire, utiliser DECIMAL ne vous donnera en aucun cas une "arithmétique à l'épreuve des échecs".

J'espère que cela t'aides.

kanap008
la source
4
Un excellent test. Il y a des années, les fonctions de conversion de données de la bibliothèque C créaient souvent des tonnes de différences infimes dans les valeurs converties d'ASCII en float par rapport à celles de, disons, SQLServer. Cela est rarement plus vrai. Les tests sont la meilleure politique, car il est préférable de savoir avec certitude quels sont les compromis.
15
En fait, l'ajout DECIMAL est une erreur. Si vous ajoutez 33,333333333 trois fois, vous n'obtenez pas 100. Si vous divisez 100 par 3, vous n'obtenez pas un nombre rationnel sans un ensemble répétitif de chiffres de fin, vous ne pouvez donc pas le multiplier par 3 et obtenir 100. Sortez. une calculatrice et essayez-la. Logiquement, nous savons que 1/3 + 1/3 + 1/3 devrait être égal à 3 / 3rds IE: 1, mais cette classe de nombres rationnels ne nous permet pas de le faire.La réponse flottante est correcte, mais votre comptable détestera cela !
user2548100
5
Est -ce pas @adonner 99,999999999000000000000000000000 DECIMAL? Ce qui est techniquement correct.
Vincent Poirier
78

Un "float" dans la plupart des environnements est un type à virgule flottante binaire. Il peut stocker avec précision les valeurs de base 2 (jusqu'à un certain point), mais ne peut pas stocker avec précision de nombreuses valeurs de base 10 (décimales). Les flotteurs sont les plus appropriés pour les calculs scientifiques. Ils ne conviennent pas à la plupart des maths orientées affaires et une utilisation inappropriée des flotteurs vous mordra. De nombreuses valeurs décimales ne peuvent pas être représentées exactement en base-2. 0.1ne peut pas, par exemple, et vous voyez donc des résultats étranges comme 1.0 - 0.1 = 0.8999999.

Les décimales stockent les nombres en base 10. Le type décimal est un bon type pour la plupart des mathématiques commerciales (mais tout type «monétaire» intégré est plus approprié pour les calculs financiers), où la plage de valeurs dépasse celle fournie par les types entiers et des valeurs fractionnaires sont nécessaires. Les décimales, comme leur nom l'indique, sont conçues pour les nombres en base 10 - elles peuvent stocker avec précision les valeurs décimales (encore une fois, jusqu'à un certain point).

Michael Petrotta
la source
@Michael Petrotta - l'utilisateur entre juste ses nombres décimaux dans le champ donné dans les formulaires .. je dois juste les stocker dans DB. ce qui sera plus approprié. ?
Hacker
12
@Pradeep: J'ai l'impression que vous ne répondez pas à mes questions. C'est peut-être parce que vous ne connaissez pas les réponses vous-même - peut-être que vous ne vous sentez pas à l'aise de demander plus de détails à votre responsable ou client. Si tel est le cas, je suggère de mordre la balle, de m'asseoir avec eux pendant quelques heures et de vraiment parcourir votre application. À quoi servent exactement et en détail vos données?
Michael Petrotta
1
En fait, actuellement, float et DECIMAL stockent leurs numéros de la même manière. La différence réside dans la manière dont ces chiffres sont utilisés. DECIMAL utilise tous les bits pour comprendre un entier complémentaire à deux, avec un point décimal implicite. Un flottant a deux entiers et l'un élève l'autre à une puissance. La base et l'exposant sont tous deux des entiers complémentaires à deux.
user2548100
1
Je pense que votre réponse peut être techniquement correcte, mais l'accent mis sur le flottant étant un type binaire obscurcit le fait qu'ils stockent tous les deux leurs données dans le même format. Un nombre à virgule flottante élevé à la première puissance est un entier, et est stocké exactement de cette façon. En fait, pour un float de précision 80 bits, la base est un int64. Inversement, si vous écriviez une bibliothèque d'entiers qui les élevaient à des puissances, vous rencontreriez les mêmes problèmes avec les entiers, ou DECIMALS, ou les nombres romains, ou les sucettes. Ce n'est pas le stockage qui crée les "erreurs d'arrondi", c'est la gestion des mathématiques par la bibliothèque.
user2548100
1
Étant donné la très mauvaise qualité de la question, où pratiquement aucun paramètre n'est donné pour indiquer quels sont les domaines de préoccupation des PO, il est difficile de savoir quelle est la réponse appropriée. En général, DECIMAL stockera de plus grands nombres et les bibliothèques mathématiques répondent aux attentes des comptables, tandis que le double flotteur est un support de stockage moins efficace qui a massivement optimisé les bibliothèques mathématiques - qui répondent beaucoup mieux aux attentes des scientifiques et des financiers (et non des comptables).
user2548100
21

MySQL a récemment changé la façon dont ils stockent le type DECIMAL . Dans le passé, ils stockaient les caractères (ou nybbles) pour chaque chiffre comprenant une représentation ASCII (ou nybble) d'un nombre - vs - un entier complémentaire à deux, ou un dérivé de celui-ci.

Le format de stockage actuel pour DECIMAL est une série d'entiers 1,2,3 ou 4 octets dont les bits sont concaténés pour créer un nombre complémentaire à deux avec un point décimal implicite, défini par vous et stocké dans le schéma de base de données lorsque vous déclarez la colonne et spécifiez sa taille DECIMAL et la position du point décimal.

À titre d'exemple, si vous prenez un entier 32 bits, vous pouvez stocker n'importe quel nombre compris entre 0 et 4 294 967 295. Cela ne couvrira de manière fiable que 999 999 999, donc si vous jetez 2 bits et utilisez (1 << 30 -1), vous n'abandonnez rien. Couvrir tous les nombres à 9 chiffres avec seulement 4 octets est plus efficace que couvrir 4 chiffres en 32 bits en utilisant 4 caractères ASCII, ou 8 chiffres nybble. (un nybble est de 4 bits, autorisant des valeurs de 0 à 15, plus que ce qui est nécessaire pour 0 à 9, mais vous ne pouvez pas éliminer ce gaspillage en passant à 3 bits, car cela ne couvre que les valeurs de 0 à 7)

L'exemple utilisé sur la documentation en ligne MySQL utilise DECIMAL (18,9) comme exemple. Il s'agit de 9 chiffres avant et 9 chiffres derrière la virgule décimale implicite, ce qui, comme expliqué ci-dessus, nécessite le stockage suivant.

Comme 18 caractères 8 bits: 144 bits

Comme 18 nybbles 4 bits: 72 bits

Sous forme de 2 entiers 32 bits: 64 bits

Actuellement, DECIMAL prend en charge un maximum de 65 chiffres, comme DECIMAL (M, D) où la plus grande valeur autorisée pour M est 65 et la plus grande valeur de D autorisée est 30.

Afin de ne pas nécessiter de blocs de 9 chiffres à la fois, des entiers inférieurs à 32 bits sont utilisés pour ajouter des chiffres en utilisant des entiers de 1,2 et 3 octets. Pour une raison qui défie la logique, des entiers signés au lieu de non signés ont été utilisés, et ce faisant, 1 bit est rejeté, ce qui donne les capacités de stockage suivantes. Pour les entiers de 1,2 et 4 octets, le bit perdu n'a pas d'importance, mais pour les entiers à 3 octets, c'est un désastre car un chiffre entier est perdu en raison de la perte de ce bit unique.

Avec un entier 7 bits: 0 - 99

Avec un entier 15 bits: 0 à 9 999

Avec un entier 23 bits: 0 - 999 999 (0 - 9 999 999 avec un entier 24 bits)

Les entiers 1, 2, 3 et 4 octets sont concaténés ensemble pour former un "pool de bits" utilisé par DECIMAL pour représenter le nombre avec précision comme un entier complémentaire à deux. Le point décimal n'est PAS stocké, il est implicite.

Cela signifie qu'aucune conversion ASCII en int n'est requise du moteur de base de données pour convertir le "nombre" en quelque chose que le CPU reconnaît comme un nombre. Pas d'arrondi, pas d'erreurs de conversion, c'est un nombre réel que le CPU peut manipuler.

Les calculs sur cet entier arbitrairement grand doivent être effectués dans le logiciel, car il n'y a pas de support matériel pour ce type de nombre, mais ces bibliothèques sont très anciennes et hautement optimisées, ayant été écrites il y a 50 ans pour prendre en charge les données à virgule flottante de précision arbitraire IBM 370 Fortran . Ils sont toujours beaucoup plus lents que l'algèbre entière de taille fixe effectuée avec du matériel entier CPU ou les calculs en virgule flottante effectués sur le FPU.

En termes d'efficacité de stockage, comme l'exposant d'un float est attaché à chaque float, spécifiant implicitement où se trouve le point décimal, il est massivement redondant, et donc inefficace pour le travail DB. Dans une base de données, vous savez déjà où le point décimal doit aller au début, et chaque ligne de la table qui a une valeur pour une colonne DECIMAL n'a besoin que de regarder la 1 & seule spécification de l'endroit où ce point décimal doit être placé, stocké dans le schéma en tant qu'arguments d'un DECIMAL (M, D) en tant qu'implication des valeurs M et D.

Les nombreuses remarques trouvées ici sur le format à utiliser pour différents types d'applications sont correctes, je ne vais donc pas insister sur ce point. J'ai pris le temps d'écrire ceci ici parce que quiconque gère la documentation en ligne MySQL liée ne comprend rien de ce qui précède et après des séries de tentatives de plus en plus frustrantes pour leur expliquer, j'ai abandonné. La présentation très confuse et presque indéchiffrable du sujet est une bonne indication de la difficulté avec laquelle ils ont compris ce qu'ils écrivaient.

En guise de dernière réflexion, si vous avez besoin d'un calcul en virgule flottante de haute précision, il y a eu d'énormes progrès dans le code en virgule flottante au cours des 20 dernières années, et la prise en charge matérielle du 96 bits et du flottant Quadruple Precision est juste autour du coin, mais il existe de bonnes bibliothèques de précision arbitraire si la manipulation de la valeur stockée est importante.

user2548100
la source
Je crois que dans l'architecture Hazwell d'Intel, il existe des opérations AVX-2 sur des entiers 256 bits, couvrant toutes les valeurs possibles que 77 chiffres pourraient représenter, qui pourraient être utilisées pour opérer directement sur les entiers de précision étendue de DECIMAL. Il peut s'avérer prudent pour Oracle de prendre en charge une nouvelle forme de DECIMAL dans le futur couvrant 77 chiffres contre 65. J'estimerais une amélioration des performances de 5 à 10 fois en utilisant du matériel plutôt que des logiciels. 2 ^ 256 = 115,792,089,237,316,195,423,570,985,008,687,907,853,269,984,665,640,564,039,457,584,007,913,129, 639,936 (78 chiffres)
Les processeurs vectoriels d'Intel prennent désormais en charge les opérations mathématiques 512 bits. Cela couvrira 154 chiffres. 2 ^ 512 = 13.407.807.929.942.597.099.574.024.998.205.846.127.479.365.820.592.393.377.723.561.443.721.764.030.073.546.976.801.874.298.166.903.427.690.031.858.186.486.050.853.753.882.811.946.569.946.433.649.006.084.096 (155 chiffres)
13

Non seulement spécifique à MySQL, la différence entre les types float et decimal est la façon dont ils représentent des valeurs fractionnaires. Les types à virgule flottante représentent des fractions en binaire, qui ne peuvent représenter que des valeurs sous forme de {m*2^n | m, n Integers}. les valeurs telles que 1/5 ne peuvent pas être représentées avec précision (sans erreur d'arrondi). Les nombres décimaux sont également limités, mais représentent des nombres comme {m*10^n | m, n Integers}. Les décimales ne peuvent toujours pas représenter des nombres comme 1/3, mais c'est souvent le cas dans de nombreux domaines courants, comme la finance, que l'on s'attend à ce que certaines fractions décimales puissent toujours être exprimées sans perte de fidélité. Puisqu'un nombre décimal peut représenter une valeur comme $0.20(un cinquième d'un dollar), il est préférable dans ces situations.

SingleNegationElimination
la source
Étant donné que les processeurs Intel effectuent toutes les opérations intermédiaires à double flottant avec une précision de 80 bits, il n'y a presque sans exception aucune erreur d'arrondi lorsque le résultat final est ramené de 80 bits à 64 bits. Même de nombreuses bibliothèques de logiciels en virgule flottante peuvent gérer ces anomalies et des centaines d'autres anomalies arithmétiques. La théorie et la pratique sont donc très divergentes dans ce domaine.
9

decimal est pour les quantités fixes comme l'argent où vous voulez un nombre spécifique de décimales. Les flottants sont pour stocker ... des nombres de précision à virgule flottante.

Skylar Saveland
la source
5
mysql> CREATE TABLE num(id int ,fl float,dc dec(5,2));
Query OK, 0 rows affected (0.00 sec)


mysql> INSERT INTO num VALUES(1,13.75,13.75);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO num VALUES(2,13.15,13.15);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM num WHERE fl = 13.15;
Empty set (0.00 sec)

mysql> SELECT * FROM num WHERE dc = 13.15;
+------+-------+-------+
| id   | fl    | dc    |
+------+-------+-------+
|    2 | 13.15 | 13.15 |
+------+-------+-------+
1 row in set (0.00 sec)

mysql> SELECT SUM(fl) ,SUM(dc)  FROM num;
+--------------------+---------+
| SUM(fl)            | SUM(dc) |
+--------------------+---------+
| 26.899999618530273 |   26.90 |
+--------------------+---------+
1 row in set (0.00 sec)


mysql> SELECT * FROM num WHERE ABS(fl -  13.15)<0.01;
+------+-------+-------+
| id   | fl    | dc    |
+------+-------+-------+
|    2 | 13.15 | 13.15 |
+------+-------+-------+
1 row in set (0.00 sec)
zloctb
la source
2

Si vous recherchez la performance et non la précision, vous devez noter que les calculs avec des flottants sont beaucoup plus rapides que les décimales

Semra
la source
2

Types à virgule flottante (valeur approximative) - FLOAT, DOUBLE

Les types FLOAT et DOUBLE représentent des valeurs de données numériques approximatives . MySQL utilise quatre octets pour les valeurs simple précision et huit octets pour les valeurs double précision.

Pour FLOAT, le standard SQL autorise une spécification facultative de la précision (mais pas de la plage de l'exposant) en bits après le mot-clé FLOAT entre parenthèses. MySQL prend également en charge cette spécification de précision facultative, mais la valeur de précision n'est utilisée que pour déterminer la taille de stockage. Une précision comprise entre 0 et 23 donne une colonne FLOAT simple précision de 4 octets. Une précision de 24 à 53 entraîne une colonne DOUBLE double précision de 8 octets.

MySQL permet une syntaxe non standard: FLOAT (M, D) ou REAL (M, D) ou DOUBLE PRECISION (M, D). Ici, «(M, D)» signifie que les valeurs peuvent être stockées avec jusqu'à M chiffres au total, dont D chiffres peuvent être après la virgule décimale. Par exemple, une colonne définie comme FLOAT (7,4) ressemblera à -999,9999 lorsqu'elle sera affichée. MySQL effectue un arrondi lors du stockage des valeurs, donc si vous insérez 999.00009 dans une colonne FLOAT (7,4), le résultat approximatif est 999.0001.

Étant donné que les valeurs à virgule flottante sont approximatives et ne sont pas stockées en tant que valeurs exactes, les tentatives de les traiter comme exactes dans les comparaisons peuvent entraîner des problèmes. Ils sont également soumis à des dépendances de plate-forme ou d'implémentation.

Pour une portabilité maximale, le code nécessitant le stockage de valeurs de données numériques approximatives doit utiliser FLOAT ou DOUBLE PRECISION sans spécification de précision ou de nombre de chiffres.

https://dev.mysql.com/doc/refman/5.5/en/floating-point-types.html

Problèmes avec les valeurs à virgule flottante

Les nombres à virgule flottante sont parfois source de confusion car ils sont approximatifs et ne sont pas stockés sous forme de valeurs exactes . Une valeur à virgule flottante telle qu'elle est écrite dans une instruction SQL peut ne pas être la même que la valeur représentée en interne. Les tentatives de traiter les valeurs à virgule flottante comme exactes dans les comparaisons peuvent entraîner des problèmes. Ils sont également soumis à des dépendances de plate-forme ou d'implémentation. Les types de données FLOAT et DOUBLE sont soumis à ces problèmes. Pour les colonnes DECIMAL, MySQL effectue des opérations avec une précision de 65 chiffres décimaux, ce qui devrait résoudre les problèmes d'inexactitude les plus courants.

L'exemple suivant utilise DOUBLE pour montrer comment les calculs effectués à l'aide d'opérations en virgule flottante sont sujets à une erreur en virgule flottante.

mysql> CREATE TABLE t1 (i INT, d1 DOUBLE, d2 DOUBLE);
mysql> INSERT INTO t1 VALUES (1, 101.40, 21.40), (1, -80.00, 0.00),
    -> (2, 0.00, 0.00), (2, -13.20, 0.00), (2, 59.60, 46.40),
    -> (2, 30.40, 30.40), (3, 37.00, 7.40), (3, -29.60, 0.00),
    -> (4, 60.00, 15.40), (4, -10.60, 0.00), (4, -34.00, 0.00),
    -> (5, 33.00, 0.00), (5, -25.80, 0.00), (5, 0.00, 7.20),
    -> (6, 0.00, 0.00), (6, -51.40, 0.00);

mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b
    -> FROM t1 GROUP BY i HAVING a <> b;

+------+-------+------+
| i    | a     | b    |
+------+-------+------+
|    1 |  21.4 | 21.4 |
|    2 |  76.8 | 76.8 |
|    3 |   7.4 |  7.4 |
|    4 |  15.4 | 15.4 |
|    5 |   7.2 |  7.2 |
|    6 | -51.4 |    0 |
+------+-------+------+

Le résultat est correct. Bien que les cinq premiers enregistrements semblent ne pas répondre à la comparaison (les valeurs de a et b ne semblent pas être différentes), ils peuvent le faire car la différence entre les nombres apparaît autour de la dixième décimale environ, en fonction de facteurs. comme l'architecture de l'ordinateur ou la version du compilateur ou le niveau d'optimisation. Par exemple, différents processeurs peuvent évaluer différemment les nombres à virgule flottante.

Si les colonnes d1 et d2 avaient été définies comme DECIMAL plutôt que DOUBLE, le résultat de la requête SELECT n'aurait contenu qu'une seule ligne - la dernière ci-dessus.

La manière correcte d'effectuer une comparaison de nombres à virgule flottante est de décider d'abord d'une tolérance acceptable pour les différences entre les nombres, puis de faire la comparaison avec la valeur de tolérance. Par exemple, si nous convenons que les nombres à virgule flottante doivent être considérés comme identiques s'ils sont identiques avec une précision de un sur dix mille (0,0001), la comparaison doit être écrite pour trouver des différences plus grandes que la valeur de tolérance:

mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1
    -> GROUP BY i HAVING ABS(a - b) > 0.0001;
+------+-------+------+
| i    | a     | b    |
+------+-------+------+
|    6 | -51.4 |    0 |
+------+-------+------+
1 row in set (0.00 sec)

Inversement, pour obtenir des lignes où les nombres sont identiques, le test doit trouver des différences dans la valeur de tolérance:

mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1
    -> GROUP BY i HAVING ABS(a - b) <= 0.0001;
+------+------+------+
| i    | a    | b    |
+------+------+------+
|    1 | 21.4 | 21.4 |
|    2 | 76.8 | 76.8 |
|    3 |  7.4 |  7.4 |
|    4 | 15.4 | 15.4 |
|    5 |  7.2 |  7.2 |
+------+------+------+
5 rows in set (0.03 sec)

Les valeurs à virgule flottante sont soumises à des dépendances de plate-forme ou d'implémentation. Supposons que vous exécutiez les instructions suivantes:

CREATE TABLE t1(c1 FLOAT(53,0), c2 FLOAT(53,0));
INSERT INTO t1 VALUES('1e+52','-1e+52');
SELECT * FROM t1;

Sur certaines plates-formes, l'instruction SELECT renvoie inf et -inf. Sur d'autres, il renvoie 0 et -0.

Une implication des problèmes précédents est que si vous essayez de créer un esclave de réplication en vidant le contenu de la table avec mysqldump sur le maître et en rechargeant le fichier de vidage dans l'esclave, les tables contenant des colonnes à virgule flottante peuvent différer entre les deux hôtes.

https://dev.mysql.com/doc/refman/5.5/en/problems-with-float.html

Voir grand
la source
0

Règle dure et rapide

Si tout ce que vous avez à faire est d'ajouter, de soustraire ou de multiplier les nombres que vous stockez, DECIMAL est le meilleur.

Si vous avez besoin de diviser ou de faire toute autre forme d'arithmétique ou d'algèbre sur les données, vous serez presque certainement plus heureux avec float. Les bibliothèques à virgule flottante et sur les processeurs Intel, le processeur à virgule flottante lui-même, ont des tonnes d'opérations pour corriger, réparer, détecter et gérer le blizzard d'exceptions qui se produisent lors de l'exécution de fonctions mathématiques typiques - en particulier les fonctions transcendantales.

En ce qui concerne la précision, j'ai déjà écrit un système budgétaire qui calculait le pourcentage de contribution de chacun des 3000 comptes et plus, pour 3600 unités budgétaires, par mois au nœud de consolidation de cette unité, puis en fonction de cette matrice de pourcentages (3000 + x 12 x 3600) J'ai multiplié les montants budgétés par les nœuds organisationnels les plus élevés jusqu'aux 3 niveaux suivants des nœuds organisationnels, puis j'ai calculé toutes les valeurs (3000 + 12) pour les 3200 unités de détail à partir de cela. Des millions et des millions et des millions de calculs en virgule flottante double précision, dont l'un quelconque renoncerait au cumul de toutes ces projections dans une consolidation ascendante au plus haut niveau de l'organisation.

L'erreur totale en virgule flottante après tous ces calculs était de ZERO . C'était en 1986, et les bibliothèques en virgule flottante sont aujourd'hui bien meilleures qu'elles ne l'étaient à l'époque. Intel effectue tous ses calculs intermédiaires de doubles avec une précision de 80 bits, ce qui élimine pratiquement toute erreur d'arrondi. Quand quelqu'un vous dit "c'est une erreur en virgule flottante", il est presque certain que ce n'est PAS vrai.


la source
-2

float(et double) représente des fractions binaires

decimal représente les fractions décimales

ReignBough
la source
-2
declare @float as float(10)
declare @Decimal as decimal(10)
declare @Inetger as int

set @float =10.7
set @Decimal =10.7
set @Inetger=@Decimal

print @Inetger

en flottant lorsque la valeur est définie sur integer print 10 mais en décimal 11

user966380
la source