Le forum (ô combien francophone) des utilisateurs de Powerbuilder.
Bonjour,
Nous venons de migrer notre application en PB12.0 Build 5530.
Dans une datawindow, on cherche à faire un filtre sur une colonne de type number. Si je l'affiche sur 17 caractères, il m'affiche 9.450000000000003 alors que j'ai 9.45 en base de données (colonne de type float en Oracle 10.2). Si je l'affiche sur 18 caractères, le 3 disparaît.
Je filtre avec ma_colonne = 9.450000000000001, la ligne apparaît bien !?!
Je filtre avec ma_colonne = 9.45, la ligne n'apparaît plus.
Un getitemnumber sur ma_colonne ramène bien 9.45
J'ai essayé avec une autre valeur :
filtre avec ma_colonne = 26.10, la ligne apparaît
filtre avec ma_colonne = 26.100000000000001, la ligne est toujours là !?!
Sous PB10.2 build 8011 avec Oracle 8.4 tous les filtres ci-dessus fonctionnent.
Quelqu'un a déjà vu çà ?
A l'aide !
Hors ligne
abdelta a écrit:
Quelqu'un a déjà vu çà ?
A l'aide !
La version courte :
La virgule flottante (= float ou double) pour représenter des nombres décimaux, quand on ne fait pas de calcul scientifique sur des nombres très petits ou énormément grands, ça n'apporte que des ennuis.
Il est de loin préférable d'utiliser des nombre à virgule fixe (= decimal). D'autant plus si c'est pour représenter des données financières car il sur toutes les fonctions d'agrégation sur les floats se posent des problèmes d'arrondis qui font qu'on perd des centimes en route ou des euros entiers (voir plus selon la quantité de donnés)
La version longue :
La raison technique c'est que les ordis, qui ne comptent qu'avec des puissances de 2, lorsqu'ils manipulent des nombre réels dans le coprocesseur mathématique les représentent sous la forme mantisse (un petit nombre) + exposant (qui décale la virgule).
En gros, pour représenter 42 on aura 4.2E1 (4.2 x 10), pour 4200 on aura 4.2E3 (4.2 x 1000), etc.
Le problème vient qu'en interne pour représenter la mantisse, certains nombrent ne "tombent pas juste". Ils sont approchés. Avec une bonne précision tout de même, mais ça reste une approximation.
Quand j'étais encore à l'école des programmeurs, j'ai eu à faire un projet de calculette (en assembleur) et j'ai pu expérimenter certains de ces nombres : par exemple 1.2 était stocké 1.19999999... et 3.8 donnait 3.7999999... Lors de l'affichage, les valeurs sont arrondies et on ne se rend pas toujours compte de la "supercherie". Le nombre de décimales dépend du type de réel (float = simple précision, double = double précision) mais on peut déjà voir le problème.
Du coup dans le code on peut tomber sur des trucs qui paraissent aberrants du genre 9.450000000000001 = 9.450000000000003 si on arrondit les valeurs n décimales (ce qui est fait par le code à un moment ou un autre).
Pour palier à ces problèmes, on a heureusement inventé des libs de calcul en virgule fixe avec lesquelles on choisit le nombre de décimales de précision voulue et qui permettent de toujours calculer juste sans arrondi. Dans du pbscript, c'est utilisé quand on utilise decimal à la place de float : on peut par exemple choisir d'utiliser 2 chiffres après la virgule en écrivant dec{2} et on n'a plus de problème.
Maintenant toute mon explication ne répond pas forcément à ta question puisque ça semblait "marcher avant" avec les mêmes types de données dans la base. Mais je peux t'assurer que je suis sûr de ce que j'avance sur les problèmes liés aux réels et aux arrondis et que chez nous on a banni tout le code qui utilisait float ou double pour utiliser exclusivement decimal. Par ailleurs dans nos bases on n'a que du numeric (Sybase SQL Anywhere) et aucun réel.
Si tu le peux, je t'invite fortement à changer de type de valeur numérique...
Pour étayer mes propos, tu peux consulter wikipedia sur la virgule flottante, notamment les précautions d'emploi inhérentes.
En espérant aider...
Hors ligne
seki a tout dit, ou presque. J'ajouterai un autre lien que j'aime bien sur le sujet, en anglais malheureusement : Floating-point guide
Pour ceux (dont je fais partie...) qui n'ont pas la chance d'avoir pu bannir l'arithmétique flottante de leurs applications de gestion, deux techniques palliatives :
1) Conversion à la volée vers des types décimaux. La précision devra être suffisante pour les calculs mais assez faible pour ne pas prendre en compte la micro-anomalie générée par le stockage flottant dans les profondeurs des décimales. La conversion devra se faire dès lecture de la base, avant toute manipulation qui amplifierait l'erreur.
2) Utiliser des filtres "permissifs" utilisant des expressions du genre "abs(data - cible) < 0.00001".
Dans les deux cas, ce n'est pas beau, mais ça permet de se débrouiller. La meilleure solution reste de changer les types de données à la source. Encore faut-il pouvoir mobiliser les ressources nécessaires pour ce genre de projet... (soupir)
Hors ligne