2008-10-08 3 views
3

AFAIK, le type de devise dans Delphi Win32 dépend de la précision du flottant du processeur. Pour cette raison, j'ai des problèmes d'arrondis lorsque je compare deux valeurs de devises, renvoyant des résultats différents en fonction de la machine. Pour l'instant, j'utilise la fonction SameValue en passant un paramètre Epsilon = 0.009, car je n'ai besoin que de 2 chiffres décimaux de précision.Comment éviter les problèmes d'arrondi lors de la comparaison des valeurs de devise dans Delphi?

Y a-t-il une meilleure façon d'éviter ce problème?

Répondre

14

Le type Currency dans Delphi est un entier de 64 bits mis à l'échelle de 1/10 000; en d'autres termes, son plus petit incrément est équivalent à 0,0001. Il n'est pas sensible aux problèmes de précision de la même manière que le code à virgule flottante.

Toutefois, si vous multipliez vos nombres monétaires par des nombres à virgule flottante ou si vous divisez vos valeurs monétaires, l'arrondi doit être calculé d'une manière ou d'une autre. La FPU contrôle ce mécanisme (c'est ce qu'on appelle le "mot de contrôle"). L'unité Math contient certaines procédures qui contrôlent ce mécanisme: SetRoundMode en particulier. Vous pouvez voir les effets de ce programme:

{$APPTYPE CONSOLE} 

uses Math; 

var 
    x: Currency; 
    y: Currency; 
begin 
    SetRoundMode(rmTruncate); 
    x := 1; 
    x := x/6; 
    SetRoundMode(rmNearest); 
    y := 1; 
    y := y/6; 
    Writeln(x = y); // false 
    Writeln(x - y); // 0.0001; i.e. 0.1666 vs 0.1667 
end. 

Il est possible qu'une bibliothèque tierce que vous utilisez est mise le mot de commande à une valeur différente. Vous pouvez définir explicitement le mot de contrôle (c'est-à-dire le mode d'arrondi) au point de départ de vos calculs importants. En outre, si vos calculs sont transférés en virgule flottante et ensuite en devise, tous les paris sont désactivés, ce qui est trop difficile à vérifier. Assurez-vous que tous vos calculs sont en devise.

+0

Méfiez-vous que l'arithmétique entre les types de devises se fait en utilisant des instructions à virgule flottante. Le résultat est susceptible d'erreurs d'arrondi en raison de la représentation binaire en virgule flottante. –

-2

Pour éviter d'éventuels problèmes d'arrondi des devises dans Delphi, utilisez 4 décimales. Cela permettra de ne jamais avoir de problèmes d'arrondis lors de calculs avec de très petites quantités.

"Been there. Done That. Written the unit tests."

+1

Wow. Ai-je vraiment écrire cela. Tenté de supprimer cela, mais il sert d'exemple de quoi ne pas faire. ;) –

12

Non, la monnaie est pas un type à virgule flottante. C'est un nombre décimal à précision fixe, implémenté avec un stockage d'entier. Il peut être comparé exactement, et n'a pas les problèmes d'arrondis de, disons, Double. Par conséquent, si vous voyez des valeurs inexactes dans vos variables Devise, le problème n'est pas le type de devise lui-même, mais ce que vous y mettez. Très probablement, vous avez un calcul à virgule flottante ailleurs dans votre code. Puisque vous ne montrez pas ce code, il est difficile d'être plus utile sur cette question. Mais la solution, en général, sera d'arrondir vos nombres à virgule flottante à la précision correcte avant de les stocker dans la variable Devise, plutôt que d'effectuer une comparaison inexacte sur les variables de devise.

+2

A partir de l'aide Delphi: Type de devise: Un numéro de devise de 8 octets (64 bits) est stocké sous la forme d'un entier 64 bits mis à l'échelle et signé avec les quatre chiffres les moins significatifs représentant implicitement quatre décimales. –

+0

La multiplication de deux valeurs de devise est effectuée avec des instructions à virgule flottante dans le processeur. Le résultat est vulnérable aux erreurs d'arrondi. –

0

Si votre situation est comme la mienne, vous pourriez trouver cette approche utile. Je travaille principalement dans la paie. Si une entreprise a trois ministères et veut répartir équitablement le coût d'un employé entre ces trois ministères, il y aura souvent des problèmes d'arrondis. Ce que j'ai fait, c'est faire une boucle dans les ministères en facturant chacun un tiers du coût total et en ajoutant le coût imputé à une variable de sous-total (devise). Mais lorsque la variable de boucle est égale à la limite, plutôt que de la multiplier par la fraction, je soustrais la variable de sous-total du coût total et la place dans le dernier département. Puisque les entrées de journal qui résultent de ce processus doivent toujours être équilibrées, je crois que cela a toujours fonctionné.

0

Voir fil:

D7/DUnit: tous CheckEquals (Monnaie,) tests échouent soudainement ...

https://forums.codegear.com/thread.jspa?threadID=16288

Il ressemble à un changement sur nos postes de travail de développement a causé à devises comparision échouer. Nous n'avons pas trouvé la cause, mais sur deux ordinateurs exécutant Windows 2000 SP4, et indépendamment de la version de gds32.dll (Interbase 7.5.1 ou 2007) et Delphi (7 et 2009), cette ligne

TIBDataBase.Create(nil); 

change la valeur du mot de contrôle 8087 de $ 1372 à $ 1272 maintenant.

Et toutes les devises comparisions dans les tests unitaires échouent avec des messages drôles comme

Expected: <12.34> - Found: <12.34> 

gds32.dll n'a pas été modifié, donc je suppose qu'il ya une dépendance dans cette bibliothèque à un dll tiers qui modifie le mot de contrôle.

1

plus rapide et plus sûr moyen de comparer deux valeurs currency est certainement pour cartographier les variables à leur représentation Int64 interne:

function CompCurrency(var A,B: currency): Int64; 
var A64: Int64 absolute A; 
    B64: Int64 absolute B; 
begin 
    result := A64-B64; 
end; 

Cela évitera toute erreur d'arrondi lors de la comparaison (travail avec * 10000 valeurs entières), et sera plus rapide que l'implémentation FPU par défaut (en particulier sous le compilateur XE2 64 bits). Pour plus d'informations, voir this article pour plus d'informations.

Questions connexes