2009-10-21 7 views
3

Je suis en train d'effectuer certaines conversions de type de données où je dois représenter uint, long, ulong et decimal en tant que valeurs à virgule flottante IEEE 754. Je veux être en mesure de détecter si le type de données IEEE 754 ne peut pas contenir la valeur avant d'effectuer la conversion.Comment tester si la conversion numérique va changer la valeur?

Une solution de force brute serait d'enrouler un try-catch autour d'un plâtre pour doubler la recherche de OverflowException. La lecture de certains des CLR documentation implique que certaines conversions changent silencieusement la valeur sans aucune exception.

Y at-il un moyen infaillible de faire cette vérification? Je suis à la recherche de complétude sur la facilité de mise en œuvre. Je sens que je vais être lire attentivement l'IEEE 754 spécifications et le contrôle Matissa et l'exposant soigneusement ...

Je dois ajouter que je suis le plus concerné de représenter des nombres entiers avec précision et que la perte de flotter la précision ponctuelle est secondaire (mais mérite d'être considérée).

EDIT: Int32 peut être entièrement exprimé en tant que IEE-754. Le type de données Decimal fait également partie de la question.

Mise à jour importante: si vous faites référence à cette question, vous devriez également lire cette question: IEEE-754 Double (64-bit floating point) vs. Long (64-bit Integer) Revisited

Il note une faille dans la réponse où certaines valeurs très importantes sont également en mesure d'être exactement représenté par l'IEEE -754. Bien que cela puisse signifier que la valeur sera correctement aller-retour, pour mon but original (va-t-il aller-retour à JavaScript), il ne sera pas.

Il semble également y avoir un bogue dans le type CLR System.Double car il n'autorise pas correctement ces valeurs à aller-retour.

+0

Si vous voulez éviter le débordement, vous pouvez vérifier que votre valeur est comprise entre MinValue et MaxValue du type cible avant la conversion. Si vous voulez éviter le problème de précision, n'utilisez pas de nombres à virgule flottante. – Guillaume

Répondre

9

solution simple pourrait être quelque chose comme (si x est un entier):

if ((int)(double)x != x) { 
    // won't convert 
} else { 
    // will convert 
} 

et ainsi de suite pour longtemps, etc.

(double) x convertira x d'un int un double. Le (int) le convertit ensuite à nouveau. So (int) (double) x convertit un int en un double puis en retour. Essentiellement le code vérifie que la conversion en double est réversible (et donc que le double peut stocker la valeur exacte de l'int).

+0

Je ne sais pas pourquoi cela a été downvoted - il semble que la solution la plus simple et la plus évidente pour moi. –

+0

+1 - C'est une réponse simple, vous pouvez mettre la clarification "if x is an int" en gras, car il sera facilement manqué. –

+0

Vous pouvez expliquer comment (int) (double) fonctionnerait, car ce n'est pas un casting commun, et sauf pour le commentaire de Jon Skeet, on peut supposer que cela ne fonctionnera pas. –

1

Cela dépend principalement de la plage de numéros avec laquelle vous travaillez. Tant que vous êtes dans les 15 chiffres (pour un double), vous devriez être du bon côté pour les nombres entiers.

Fondamentalement, ce que vous devez prendre en compte est le nombre de chiffres significatifs. Donc, tant que votre nombre est inférieur à la limite numérique, il restera exact; si elle devient plus grande, vous perdrez la précision (même si ce sont des nombres entiers).

Donc, tant que votre numéro est < 2^53, vous êtes généralement bon.

1

IEEE 754 Double a 52 bits pour la mantisse et vous convertissez de/à entier/long donc assez facile à tester. Si votre nombre entier consomme moins de 52 bits alors il devrait être convertible sans problème en double IEEE 754.

Je suppose (je sais avec certitude dans le cas de Java mais pas C# et paresseux à vérifier) ​​que int est 32 bits et long est 64 bits. Donc, bien sûr, int peut s'adapter en double sans aucun problème à la fois signe et non signé. Pour ulong, vous simplement si tous les bits supérieurs au 52ème bit est un ((aULong & & 0xFFF0000000000000) == 0).

Pour longtemps, vous devez apporter son signe à la considération. Comme Long est 2ème complément mais que IEEE754 ne l'est pas (il suffit d'avoir un bit négatif), il pense qu'il est sûr de convertir simplement négatif long à positif (* -1) et de vérifier comme positif. Donc, si le long est négatif, chronométrez avec -1 en premier (ne faites rien pour le positif). Ensuite, vérifiez comme ulong.

Espérons que cela aide.

Questions connexes