Question StrToFloat ne parvient pas à signaler 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'elle est compilée avec le compilateur Windows 64 bits, elle renvoie silencieusement un 0 (zéro).

Est-il possible que StrToFloat signale une erreur lorsque la valeur à virgule flottante est incorrecte?

J'ai essayé TArithmeticException.exOverflow, mais cela n'a aucun effet dans ce cas.

J'ai également essayé TArithmeticException.exPrecision mais il se déclenche dans de nombreux cas d'approximation habituels (il se déclenche lors de la conversion de '1e9').

Le problème a été remarqué avec Delphi 10.2 update 3

Addenda: pour contourner le problème, j'ai commencé une implémentation alternative de string à double conversion en salle blanche, la version initiale avec des tests peut être trouvée dans dwscript commettre 2ba1d4a


22
2018-04-09 15:14


origine


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 de l'exposant max en dehors de la boucle, la valeur du résultat de l'entier signé de 16 bits est trop petite pour votre exposant de 99999999. Lorsque l'exposant est lu, la valeur dans Result déborde et devient négatif. Donc, pour votre exemple, il s'avère qu'un exposant de -7937 est utilisé plutôt que 99999999. Cela conduit naturellement à une valeur de zéro.

Ceci est un bug clair et j'ai envoyé un rapport de bogue: RSP-20333.

En ce qui concerne la manière de contourner le problème, je ne connais pas d'autre fonction dans la RTL Delphi qui effectue cette tâche. Donc, je pense que vous devrez faire l'une des choses suivantes:

  • Rouler le vôtre StrToFloat.
  • Prétraitez la chaîne et gérez les exposants avant qu'ils ne soient lus 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 que je peux maintenant le réparer!

Mettre à jour:

Vous pourriez également être intéressé par un bug connexe que j'ai trouvé lors d'une enquête: RSP-20334. Cela pourrait vous surprendre de réaliser cela, StrToFloat('߀'), lorsque vous utilisez la version PUREPASCAL de StrToFloat, résultats 1936.0. L'astuce est que le personnage qui est passé à StrToFloat est un chiffre non latin, dans ce cas U + 07C0.


23
2018-04-09 15:43