2 votes

sprintf : virgule fixe, grands nombres et perte de précision

Je dois lire des nombres dans une base de données et les écrire dans un fichier texte en utilisant Perl.

Dans le tableau où figurent les nombres, le format des données est défini comme suit numeric (25,5) (il lit 25 chiffres, dont 5 décimales).

Je formate les nombres dans mon fichier avec un sprintf "%.5f", $myvalue pour forcer 5 décimales et je viens de remarquer que pour les grandes valeurs, il y a une perte de précision pour les nombres avec plus de 17 chiffres :

db   = 123.12345
file = 123.12345 (OK)

db   = 12345678901234891.12345
file = 12345678901234892.00000 (seems to be rounded to upper integer)

db   = 12345678901234567890.12345
file = 12345678901234567000.00000 (truncation ?)

Quelle est la plus grande précision de Perl pour les nombres décimaux fixes ?

Je suis conscient des concepts et des limites de l'arithmétique à virgule flottante en général, mais je ne suis pas un moine Perl et je ne connais pas les rouages de Perl, donc je ne sais pas si c'est normal (ou si c'est lié du tout à la virgule flottante). Je ne sais pas non plus s'il s'agit d'une limitation interne de Perl, ou d'un problème lié à la fonction sprintf traitement.

Existe-t-il une solution de contournement ou un module dédié qui pourrait aider à résoudre ce problème ?

Quelques points notables :

  • il s'agit d'une fonctionnalité supplémentaire d'un système qui utilise déjà Perl, de sorte que l'utilisation d'un autre outil n'est pas envisageable
  • les données traitées sont financières, je dois donc conserver chaque centime et je ne peux pas m'accommoder d'une précision de +/- 10 000 unités :^S

2voto

Seki Points 5620

Une fois de plus, je trouve une solution juste après avoir demandé à SO. Je mets ma solution ici, pour aider un futur visiteur :

remplacer

$myout = sprintf "%.5f", $myvalue;

par

use Math::BigFloat;
$myout = Math::BigFloat->new($myvalue)->ffround( -5 )->bstr;

2voto

jm666 Points 17312

Sans des modules comme Math::BigFloat, tout ce qui est supérieur à 16 chiffres est de la pure magie... par exemple.

perl -e 'printf "*10^%02d: %-.50g\n", $_, log(42)*(10**$_) for (0..20)'

produit

*10^00: 3.7376696182833684112267746968427672982215881347656
*10^01: 37.376696182833683224089327268302440643310546875
*10^02: 373.76696182833683224089327268302440643310546875
*10^03: 3737.6696182833684360957704484462738037109375
*10^04: 37376.6961828336861799471080303192138671875
*10^05: 373766.96182833681814372539520263671875
*10^06: 3737669.6182833681814372539520263671875
*10^07: 37376696.18283368647098541259765625
*10^08: 373766961.82833683490753173828125
*10^09: 3737669618.283368587493896484375
*10^10: 37376696182.83368682861328125
*10^11: 373766961828.33685302734375
*10^12: 3737669618283.36865234375
*10^13: 37376696182833.6875
*10^14: 373766961828336.8125
*10^15: 3737669618283368.5
*10^16: 37376696182833688
*10^17: 373766961828336832
*10^18: 3737669618283368448
*10^19: 37376696182833684480
*10^20: 373766961828336828416

1voto

ikegami Points 133140

Quelle est la plus grande précision de Perl pour les nombres décimaux fixes ?

Perl ne dispose pas de nombres décimaux à virgule fixe. Très peu de langages le font, en fait. Vous pourriez utiliser un module comme Math::FixedPoint mais

1voto

Michael Carman Points 21983

Perl stocke vos valeurs sous forme de nombres à virgule flottante en interne. 1 La précision dépend de la façon dont votre version de Perl est compilée, mais il s'agit probablement d'un double 64 bits.

C:\>perl -MConfig -E "say $Config::Config{doublesize}"
8

Un flottant double précision de 64 bits 2 a un significand de 53 bits (fraction ou mantisse), ce qui lui confère une précision d'environ 16 caractères décimaux. Votre base de données est définie comme stockant 25 caractères de précision. Tout ira bien si vous traitez les données comme une chaîne, mais si vous les traitez comme un nombre, vous perdrez en précision.

Le système Perl bignum fournit un support transparent pour les nombres arbitrairement grands. Il peut ralentir considérablement les choses, c'est pourquoi il faut limiter son utilisation au plus petit champ d'application possible. Si vous voulez uniquement des grands nombres flottants (sans rendre les autres types numériques "grands"), utilisez Math::BigFloat au lieu de cela.

1. En interne, perl utilise un type de données appelé SV qui peut contenir simultanément des flottants, des ints et/ou des chaînes de caractères.<br>2. En supposant un format IEEE 754.

0voto

Yary Points 305

Par ailleurs, si vous transférez simplement les valeurs de la base de données vers un fichier texte et que vous ne les traitez pas comme des nombres, demandez à la base de données de les formater comme des chaînes de caractères. Ensuite, lisez-les et imprimez-les sous forme de chaînes (en utilisant par exemple "printf '%s'"). Par exemple :

select Big_fixed_point_col(format '-Z(24)9.9(5)')(CHAR(32))

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X