StrToFloat ne signale pas les nombres à virgule flottante non valides dans Delphi 64bits
Le code suivant qui tente de convertir une valeur bien au-delà de la plage de double précision
StrToFloat('1e99999999')
Signale correctement une valeur à virgule flottante incorrecte Dans Delphi 10. 2r3 avec le compilateur Windows 32 bits, mais lorsqu'il est compilé avec le compilateur Windows 64 bits, il renvoie silencieusement un 0 (zéro).
Existe-t-il un moyen D'avoir StrToFloat signaler une erreur lorsque la valeur à virgule flottante est incorrecte?
J'ai essayé TArithmeticException.exOverflow, mais cela n'a aucun effet dans que cas.
J'ai aussi essayé TArithmeticException.expressision mais il se déclenche dans de nombreux cas d'approximation habituels (par exemple, il se déclenche lors de la conversion '1e9').
Problème a été remarqué avec Delphi 10.2 update 3
Addendum: pour contourner le problème, j'ai commencé une implémentation alternative de string à double conversion, la version initiale avec des tests peut être trouvée dans dwscript commit 2ba1d4a
1 réponses
Ceci est un défaut qui est présent dans toutes les versions de Delphi qui utilisent la version PUREPASCAL de StrToFloat
. Cela correspond à InternalTextToExtended
qui lit l'exposant comme ceci:
function ReadExponent: SmallInt;
var
LSign: SmallInt;
begin
LSign := ReadSign();
Result := 0;
while LCurrChar.IsDigit do
begin
Result := Result * 10;
Result := Result + Ord(LCurrChar) - Ord('0');
NextChar();
end;
if Result > CMaxExponent then
Result := CMaxExponent;
Result := Result * LSign;
end;
Le problème est l'emplacement de
if Result > CMaxExponent then
Ce test est censé être à l'intérieur de la boucle, et dans la version asm x86 de ce code, il l'est. Comme codé ci-dessus, avec le test d'exposant Maximum en dehors de la boucle, la valeur de résultat entier signé de 16 bits est trop petite pour votre exposant de 99999999
. Comme l'exposant est lu, la valeur dans Result
déborde et devient négative. Alors, pour ton exemple, il s'avère qu'un exposant de -7937
plutôt que 99999999
. Naturellement, cela conduit à une valeur de zéro.
Ceci est un bug clair et j'ai soumis un rapport de bug: RSP-20333.
Quant à la façon de contourner le problème, Je ne suis pas au courant d'une autre fonction dans le Delphi RTL qui effectue cette tâche. Donc, je pense que vous devrez faire l'une des opérations suivantes:
- Rouler votre propre
StrToFloat
. - pré-traiter la chaîne et gérer les exposants hors de portée avant de lire
StrToFloat
. - utilisez l'une des fonctions de la bibliothèque d'exécution C qui effectue la même tâche.
Enfin, je vous suis reconnaissant de poser cette question car je peux voir que mon propre programme est affecté par ce défaut et donc je peux maintenant le réparer!
Mise à Jour:
Vous pouvez également être intéressé de regarder un bug connexe que j'ai trouvé lors de l'enquête: RSP-20334. Cela pourrait vous surprendre de réaliser que, StrToFloat('')
, lorsque vous utilisez la version PUREPASCAL de StrToFloat
, renvoie 1936.0
. L'astuce est que le caractère qui est passé à StrToFloat
est un chiffre non Latin, dans ce cas U + 07C0 .