11
votes

Comment puis-je faire de la signalisation NANS facile à travailler?

La norme IEEE754 définit deux classes de NaN, la Nan tranquille, le QNAN et la Nan de signalisation, SNAN. Lorsqu'un SNAN est chargé dans un registre à point flottant, une exception est soulevée par l'unité à point flottant.

QNAN est disponible pour DelphI Code à travers la constante nommée NAN qui est déclaré dans Math . La définition de cette constante est la suivante: xxx

J'aimerais pouvoir utiliser quelque chose de similaire pour déclarer une constante qui est une nan de signalisation, mais n'a pas encore trouvé de moyen de faire ça.

naïvement, vous pouvez écrire ce code: xxx

mais l'ABI pour les valeurs de retour de point flottant signifie que le SNAN est chargé dans un point flottant inscrivez-vous pour que cela puisse être retourné. Naturellement, cela conduit à une exception qui défait plutôt le but.

Vous êtes alors amené à écrire du code comme celui-ci: xxx

maintenant, cela fonctionne, Mais c'est très gênant. Supposons que vous ayez besoin de passer un SNAN à une autre fonction. Idéalement, vous souhaitez écrire: xxx

mais vous devez le faire: xxx

donc, après la construction -p, voici la question.

existe-t-il un moyen d'écrire x: = snan et avoir la variable de point flottant x attribué une valeur qui est attribuée une nan de signalisation?


2 commentaires

Avez-vous essayé d'inliquer votre première approche?


@Uweraabe en fait j'ai. Je voulais laisser quelqu'un d'autre à écrire cette réponse afin de pouvoir obtenir le représentant. Je me sens toujours un peu inquiet de s'appuyer sur l'inlinage. Si l'appel de la fonction n'est pas inlincé, alors flèche.


5 Réponses :


4
votes

Vous pouvez aligner la fonction: xxx pré>

mais cela dépendra de l'ambiance d'optimisation et du compilateur. P>

J'ai vu certaines fonctions non définies, sans aucune clair compréhension du contexte. Je n'aime pas ne pas s'appuyer sur l'inlinisation. P>

Qu'est-ce que je ferais mieux de faire et qui fonctionnera sur toutes les versions de Delphi, est d'utiliser une variable globale: p>

backup := Set8087CW($133F);
try
  ..
finally
   Set8087CW(backup);
end;


6 commentaires

Élever l'exception l'exception dans l'ensemble des nan de signalisation?


Dans mon expérience, si la fonction est définie à la section Démarrage de la section de mise en œuvre de l'unité compilée d'abord , la fonction sera toujours inlinée.


En ce qui concerne les tests de SNAN, il existe plusieurs modèles de bits SNAN. Donc, vous avez besoin d'un meilleur test. La fonction ISNAN en mathématiques est un bon point de départ.


@DavidHeffernan Mais je suppose que vous souhaitez principalement tester si la valeur SNAN a été définie par votre propre code, non? Dans ce cas, le test du motif bit SNAN64 est suffisant dans la pratique. Je voulais répondre à votre question, pas toutes les entreprises. :) Mais +1 sur votre commentaire.


@Uweraabe levant l'exception ... mais dans le cadre de la fonction, en ce qui concerne la question de la question. Vous ne pouvez pas me blâmer, car j'ai apporté la portée d'exception explicite et a donné une solution de contournement. C'est pourquoi j'ai ajouté des détails sur le registre de l'exception X87.


@Arnaudbouchez avez raison, en vrai code, il s'agira toujours de votre modèle de bit spécifique que vous testez. Et +1 pour la réponse.



4
votes

Voici une autre solution de contournement:

type
  ISet8087CW = interface
  end;

  TISet8087CW = class(TInterfacedObject, ISet8087CW)
  protected
    OldCW: Word;
  public
    constructor Create(const NewCW: Word);
    destructor Destroy; override;
  end;

  TIEEE754 = record
    case Byte of
      0: (SNaN: Double);
      1: (i: Int64);
  end;

const
  IEEE754: TIEEE754 = ( i: $7FF7FFFFFFFFFFFF);

{ TISet8087CW }

constructor TISet8087CW.Create(const NewCW: Word);
begin
  OldCW := Get8087CW;
  Set8087CW(NewCW);
  inherited Create;
end;

destructor TISet8087CW.Destroy;
begin
  Set8087CW(OldCW);
  inherited;
end;

procedure TForm6.Button4Click(Sender: TObject);
var
  CW: ISet8087CW;
begin
  CW := TISet8087CW.Create($133F);
  Memo1.Lines.Add(Format('SNaN: %f', [IEEE754.SNaN]));
end;


7 commentaires

On dirait que le motif Raii est en train de décoller dans la terre de Delphes. ;-)


+1 J'aime assez la variante. En passant, saviez-vous que set8087cw n'est pas threadsafe?


@Warrenp: D J'aime cette approche ici, mais aussi quand, par exemple, montrant un curseur de sablier. Tout ce qui se passe (exception, etc.), dès que l'interface manque de portée, nous allons revenir au réglage précédent.


Un problème avec une durée de vie contrôlée par interface en général à Delphi est que la plus petite unité de portée est la procédure. C'est trop grossière. Cela fonctionne bien en C ++ car les blocs introduisent de nouvelles étendues.


@Remko Je ne pense pas que l'une des fonctions RTL sur l'appel X86 Set8087CW. Mais les implémentations de certains sur x64 font (puits, SETMXSCR sur x64 qui a la même faille que SET8087CW). Ce qui signifie que les fonctions RTL standard standard dans un thread peuvent modifier l'état de l'unité FP dans une autre. C'est un vrai choc. J'ai écrit à ce sujet ici: QC.EMBARCADERO.COM/WC/QCMAIN.ASPX? D = 107411


@DavidHeffernan, j'ai essayé de télécharger le document ci-joint dans votre rapport QC, mais le fichier a été stocké en tant que fichier .htm. Renommer le fichier à .zip corrigé le problème. Excellente analyse et proposition BTW.


@Lurd Je suppose que nous avons besoin d'un document similaire pour proposer des corrections à QC !!



0
votes

J'utilise une fonction: xxx


2 commentaires

C'est un qnan mais la question concerne SNAN


Ah désolé. Je n'ai pas compris la différence.



0
votes

Voici une façon plutôt sale de le faire, ce qui entraîne un code très propre pour le consommateur.

unit uSNaN;

interface

const
  SNaN: Double=0.0;//SNaN value assigned during initialization

implementation

initialization
  PInt64(@SNaN)^ := $7FF7FFFFFFFFFFFF;

end.


0 commentaires

8
votes

Cette déclaration résout à la compilation: xxx

Le compilateur traite toujours le snan comme une constante.

Essayer d'assigner un Valeur to snan donnera une erreur d'heure de compilation: E2064 Le côté gauche ne peut pas être attribué à . xxx

Si vous avez la directive compileuse $ witeableconsts sur (ou $ j + ), cela pourrait être désactivé temporairement Pour ne pas modifier snan . xxx


2 commentaires

+1 Cela fait l'affaire, mais je ressentirais un peu de maquette sur la valeur qui vivant dans une variable plutôt qu'une constante.


L'attribution d'une valeur à SNAN donnera une erreur de compilateur: E2064 Le côté gauche ne peut pas être attribué à .