Réponses:
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.
@a
donner 99,999999999000000000000000000000 DECIMAL? Ce qui est techniquement correct.
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.1
ne 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).
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.
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.
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.
J'ai trouvé cela utile:
En général, les valeurs flottantes sont bonnes pour les calculs scientifiques, mais ne doivent pas être utilisées pour les valeurs financières / monétaires. Pour les mathématiques orientées métier, utilisez toujours Decimal.
Source: http://code.rohitink.com/2013/06/12/mysql-integer-float-decimal-data-types-differences/
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)
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
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.
float
(et double
) représente des fractions binaires
decimal
représente les fractions décimales
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
FLOAT(m,n)
, cela conduit à deux arrondis; en attendant, il n'apporte aucune utilité.