2010-04-02 12 views
17

J'ai rencontré un problème en introduisant des colonnes à virgule flottante dans le schéma de base de données MySQL que les comparaisons sur les valeurs à virgule flottante ne retournent pas toujours les bons résultats.Problèmes de comparaison à virgule flottante MySQL

1 à 50,12
2 à 34,57
3 à 12,75
4 - ... (tout repos inférieur à 12,00)

SELECT COUNT(*) FROM `users` WHERE `points` > "12.75" 

Cela me retourne "3". J'ai lu que les comparaisons de valeurs à virgule flottante dans MySQL est une mauvaise idée et le type décimal est la meilleure option.

Ai-je l'espoir d'aller de l'avant avec le type à flotteur et de faire en sorte que les comparaisons fonctionnent correctement?

+0

Quels types de littéraux sont placés entre guillemets doubles dans SQL? – Joey

+1

Malheureusement, MySQL permet aux guillemets d'agir comme des guillemets simples par défaut. Cette fonctionnalité peut être désactivée avec l'option 'ANSI_QUOTES', ce qui les fera se référer aux identifiants selon le standard ANSI SQL (comme les backticks non standard dans la requête ci-dessus). – bobince

+1

12.75 est exactement représentable en binaire (1100.11), donc je ne vois pas comment il passe le test "> 12.75". Êtes-vous sûr qu'il n'y a pas d'autre point> 12.75 dans votre liste? –

Répondre

23

Avez-vous remarqué le problème ci-dessous?

CREATE TABLE a (num float); 

INSERT INTO a VALUES (50.12); 
INSERT INTO a VALUES (34.57); 
INSERT INTO a VALUES (12.75); 
INSERT INTO a VALUES (11.22); 
INSERT INTO a VALUES (10.46); 
INSERT INTO a VALUES (9.35); 
INSERT INTO a VALUES (8.55); 
INSERT INTO a VALUES (7.23); 
INSERT INTO a VALUES (6.53); 
INSERT INTO a VALUES (5.15); 
INSERT INTO a VALUES (4.01); 

SELECT SUM(num) FROM a; 
+-----------------+ 
| SUM(num)  | 
+-----------------+ 
| 159.94000005722 | 
+-----------------+ 

Un 0.00000005722 supplémentaire est réparti entre certaines de ces lignes. Par conséquent, certaines de ces valeurs retourneront false par rapport à la valeur avec laquelle elles ont été initialisées.

Pour éviter des problèmes avec l'arithmétique à virgule flottante et les comparaisons, vous devez utiliser le type de données DECIMAL:

ALTER TABLE a MODIFY num DECIMAL(6,2); 

SELECT SUM(num) FROM a; 
+----------+ 
| SUM(num) | 
+----------+ 
| 159.94 | 
+----------+ 
1 row in set (0.00 sec) 
+0

Hey Daniel! Merci. J'envisage de convertir mon type de colonne en DECIMAL. –

+1

@Sharief: Si la conversion en 'DECIAML' est impossible, la seule option que je vois est de permettre une certaine tolérance pour les comparaisons en virgule flottante, de sorte que vous puissiez écrire votre requête comme suit:' SELECT COUNT (*) FROM users WHERE points> 12.75 + 0.001); '... Cependant, si la précision est primordiale, le point fixe' DECIMAL' est le chemin à parcourir. Une autre alternative à 'DECIMAL' pourrait être d'utiliser une valeur entière mise à l'échelle pour représenter vos valeurs en termes de centièmes:' 5012' au lieu de '50.12'. Il peut y avoir des situations où cela pourrait être approprié. –

+0

J'ai déjà essayé d'ajouter la tolérance, exactement comme vous l'avez mentionné, même si les résultats n'étaient jamais cohérents. –

1

C'est un flottant, alors quel est le problème? 3 pourrait être le résultat correct, dépend de ce que la base de données pense à 12.75. Est-ce 12,75 ou juste un peu plus?

Utilisez DECIMAL si vous voulez des nombres exacts.

+0

Salut Frank, pouvez-vous élaborer sur ce que vous entendez par "ce que la base de données pense à 12,75". Dois-je être en difficulté si j'ai essayé de comparer une valeur de précision à deux chiffres avec une précision à trois chiffres. Comme ... SELECT COUNT (*) FROM 'users' OERE' points'> "12.751" –

+0

@ShariefShaik Je pense que Decimal devrait résoudre ce cas, à partir de ce que d'autres expériences utilisateur. – gumuruh

1

Il y a un problème avec la comparaison des flotteurs pour l'égalité. Cela peut donner des résultats imprévus. Cela est dû à la mise en œuvre interne de l'arithmétique en virgule flottante.

0

Comparer un nombre avec une chaîne?

2

Je l'ai fait face à la question similaire une fois. Convertissez le champ 'float' en 'decimal'. Cela va définitivement résoudre le problème.

+0

Je pensais en utilisant une longueur fixe précise de flotteur déjà résolu le problème, de sorte qu'il ne ?? par exemple; je définis le flotteur (4,2). Et puis j'ai stocké la valeur de 12.50, et puis j'essaie de le comparer avec "> 12.50" déclaration. Ne serait-il pas encore échouer? – gumuruh

1

Je le fais

WHERE abs(value - 12.75)<0.001 

mais je suis d'accord, toute langue peut comparer flotter l'égalité et si les valeurs stockées égal nombre exact que vous valeurs que vous avez inséré, il ne devrait pas être question

avec seulement quelques des nombres décimaux et des valeurs de correspondance exactes, les erreurs de précision ne semblent pas être une raison évidente de telles incompatibilités dans MySQL