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

22
demandé sur Eric Grange 2018-04-09 18:14:38

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 .

23
répondu David Heffernan 2018-04-10 13:30:36