8
votes

Comparer des flotteurs dans leurs représentations de bits

dis que je veux une fonction qui prend deux flotteurs ( x et y ), et je veux les comparer à utiliser pas leur float Représentation mais plutôt leur représentation bitwise comme un 32 bits non signé int . C'est-à-dire qu'un nombre comme -495.5 a une représentation bit 0b1100001111100101110000000000000000 0xc3e5c000 comme un float , et j'ai un non signé INT avec la même représentation de bits (correspondant à une valeur décimale 3286614016 , que je me fiche de). Y a-t-il un moyen facile pour moi d'effectuer une opération comme <= sur ces flotteurs en utilisant uniquement les informations contenues dans leur non signé INT


2 commentaires

Vous pouvez le faire en tant que CPALMER mentionne ci-dessous, mais notez que la représentation intégrée non signée des flotteurs IEEE ne commandez pas de la même manière que leurs valeurs de flotteur équivalentes font. -0.0 n'est pas la même chose que 0,0 par exemple, quand ils sont comparés comme non signé INTS.


Vous avez une faute de frappe: il devrait être -459.5 , pas -495.5


4 Réponses :


2
votes

dans un mot, non. IEEE 754 pourrait permettre à certaines sortes de hacks comme celui-ci, mais ils ne fonctionnent pas tout le temps et gèrent tous les cas, et certaines plates-formes n'utilisent pas cette norme de point flottante (telle que double sur x87 ayant une précision de 80 bits en interne).

Si vous faites cela pour des raisons de performance, je vous suggère de reconsidérer fortement - s'il est plus rapide d'utiliser la comparaison entière, le compilateur le fera probablement pour vous, et si ce n'est pas le cas, vous payez un flotteur pour la conversion Plusieurs fois, lorsqu'une comparaison simple peut être possible sans déplacer les flotteurs hors des registres.


12 commentaires

Si IEEE 754 permet de les hacks comme celui-ci (et cela le fait), quand ça ne marcherait-il pas? Quels cas ne manipule-t-il pas?


Martinho: Avant que les unités ponctuelles flottantes ne soient intégrées à X86 Chips, elles avaient des désignations comme 8087, 80287 et 80387. Nous appelons donc les instructions FPU x87.


Le coprocesseur de mathématiques d'origine était une puce de compagnie, par ex. 287 Pour aller avec 80286, 387 pour aller avec 80386. Maintenant, le FPU fait partie du principal noyau de la CPU, mais elle implémente toujours l'ensemble d'instructions des anciens chips X87.


@gabe: Il existe plusieurs doubles valides qui ne traitent pas parfaitement comme des entiers, tels que des représentations standard pour NAN (pas un nombre) et une infinité positive / négative. La plupart des chiffres «normaux» trieront correctement sous IEEE 754 si je ne me trompe pas.


@gabe: Quels sont les deux ints non signés A et B pouvez-vous trouver pour qu'aucun de (A B) est vrai? Beaucoup de tels flotteurs existent.


J'ai eu l'impression qu'il rassemblait plutôt que de chercher des nans, alors je pense qu'il ira bien.


@GABE: La plupart des chiffres normaux trieront bien, oui, mais je serais prêt à parier qu'il est plus rapide de les laisser des doubles de toute façon afin qu'ils n'ont pas besoin d'être copiés entre les registres en premier. Les comparaisons sont rapides et il y a très très peu de cas où de tels hacks sont justifiés lorsqu'un FPU est disponible sur matrice.


Si vous triez des chiffres, ils sont probablement en mémoire, donc les charger dans des registres FPU pour effectuer une comparaison à virgule flottante ne sera certainement pas plus rapide que de faire la comparaison dans des registres entier 32 bits.


@gabe: à moins qu'ils ne soient déjà dans les registres FPU. Il est peu probable que quelqu'un effectue une comparaison sur deux aléatoires de floats de mémoire - elle travaille probablement avec au moins un des flotteurs au préalable.


Sur ma boîte, les comparaisons ne sont pas équivalentes, alors quelle manière le plus rapide semble plutôt plus discutable.


@Billy Oneal: Si vous triez un tableau de chiffres, ils ne sont pas que des flotteurs de mémoire aléatoire.


La chose importante à emporter est qu'il pourrait y avoir ou non une prestation de performance à une telle comparaison: le point flottant se compare est assez rapide par eux-mêmes, et il pourrait ou non une surcharge en hauteur pour la comparaison intégrale. Ajouter dans le fait que la comparaison n'est pas portable et qu'elles ne fonctionnent pas dans toutes les situations [même si vous êtes prêt à ignorer Nan et les infinités, zéro positif et négatif sont assez faciles à ne pas se tromper] et vous finissez pas avec un argument assez convaincant pour simplement faire confiance au compilateur jusqu'à ce que vous ayez donné raison de ne pas le faire.



2
votes

Peut-être que je suis mal interprété la question, mais je suppose que vous pourriez le faire:

bool compare(float a, float b)
{
    return *((unsigned int*)&a) < *((unsigned int*)&b);
}


4 commentaires

Ah, le bon vieux coerce-orthers piraters. Laid, mais puissant.


Si vous allez faire cela au moins utiliser REINERPRET_CAST pour le marquer en tant que plate-forme piratage spécifique que c'est.


@Billyonéal - La question est étiquetée comme C, C ++ et Objective-c. reterpret_cast est approprié pour seulement C ++.


@R Samuel Klatchko: Nous avons déjà battu ce cheval mort à Dennis Zickefoose 'Réponse. Je pense que les versions C ++ doivent être utilisées même en présence de telles balises, car elles sont faciles à convertir d'une coulée C ++ à une coulée de style C, mais pas toujours dans le sens inverse.



3
votes

Si vous ne vous souciez vraiment vraiment de ce que la conversion donne, ce n'est pas trop difficile. Mais les résultats sont extrêmement non portables et vous n'obtiendrez certainement certainement pas une commande qui ressemble au tout à ce que vous obtiendrez en comparant directement les flotteurs.

typedef unsigned int TypeWithSameSizeAsFloat; //Fix this for your platform

bool compare1(float one, float two)
    union Convert {
        float f;
        TypeWithSameSizeAsFloat i;
    }
    Convert lhs, rhs;
    lhs.f = one;
    rhs.f = two;
    return lhs.i < rhs.i;
}

bool compare2(float one, float two) {
    return reinterpret_cast<TypeWithSameSizeAsFloat&>(one) 
         < reinterpret_cast<TypeWithSameSizeAsFloat&>(two);
}


9 commentaires

Il est difficile d'utiliser le réinterpret_cast dans C.


@Richard Pennington: Ensuite, remplacez-le par un style C. Mais l'écriture par défaut devrait être la commande correcte plutôt que l'héritage dans la mesure du possible, étant donné que les couts de style C sont obsolètes. (Sauf si vous devez absolument avoir une compatibilité C ici)


@Billy Oneal La question a été marquée C, C ++ et Objective-c. Quelle moulage fonctionne dans tous les cas?


@Richard Pennington: Si l'auteur souhaite utiliser le code dans les langues qui ne prennent pas en charge les moulages en toute sécurité, je ne pense pas qu'il est trop difficile de convertir des moulages de style C. C ++ Style CastS doit être la liste par défaut lorsque C ++ est étiqueté, car tout C ++ peut être converti en une coulée de style C.


@Billy, il demande en fait comment faire une chose qui ne fait probablement pas ce qu'il attend de toute façon. Peu importe ce qui est utilisé qu'il utilise.


@Richard Pennington: Bill crache le coke de régime sur son moniteur


@Bill: J'espère que votre moniteur va bien. ;-)


Begone, typing inutile! REINERPRET_CAST (ONE) (Deux) . (PSST, vous devez utiliser un et deux dans convert2 .)


La commutation des pointeurs aux références est simplement une incompatibilité gratuite avec C, et il est probable que certains utilisateurs ne puissent pas la réécrire dans le style C. Les pointeurs sont une partie standard de C ++; N'ayez pas peur de les utiliser.



4
votes

Vous devez faire une comparaison signée à moins que vous assurez que toutes les valeurs d'origine étaient positives. Vous devez utiliser un type entier de la même taille que le type de point flottant d'origine. Chaque puce peut avoir un format interne différent, de sorte que la comparaison des valeurs de différentes puces, car les entiers sont les plus susceptibles de donner des résultats trompeurs.

La plupart des formats de flotteur ressemblent à ceci: sxxxmmmm

s est un bit de signe
xxx est un exposant

mmmm est la mantissa

La valeur représentée sera alors quelque chose comme: 1mmm << (xxx-k)

1mmm car il y a un bit (code> 1 1 si la valeur est zéro.

Si xxx alors ce sera un changement de droite. k est proche mais pas égal à la moitié de la plus grande valeur qui pourrait être exprimée par xxx . Il est ajusté pour la taille de la mantissie.

Tout pour dire que, ignorant nan , comparer les valeurs de points flottants tels que les entiers signés de la même taille donneront des résultats significatifs. Ils sont conçus de cette manière pour que les comparaisons de points flottants ne soient plus coûteuses que les comparaisons entière. Il existe des optimisations de compilateur pour désactiver les vérifications nan afin que les comparaisons soient des comparaisons entière droite si le format de point flottant de la puce prend en charge.

comme un entier, nan est supérieur à l'infini est supérieur aux valeurs finies. Si vous essayez un comparateur non signé, toutes les valeurs négatives seront plus grandes que les valeurs positives, telles que les entiers signés de manière non signée.


2 commentaires

Je suis d'accord. J'ai fouillé quelques tests en utilisant les valeurs non signées en conjonction avec les bits de panneau, qui, alors que le type de laid fonctionne totalement.


Un compare entier signé ne donnera généralement pas le même ordre qu'un point flottant se comparer aux nombres négatifs. La plupart des implémentations C et C ++ utilisent le complément de deux, dans lequel les valeurs négatives représentées sont dans le même ordre (par rapport à d'autres valeurs négatives) comme les interprétations non signées des mêmes bits. La plupart des représentations de points flottants utilisent une représentation de signalisation et de magnitude, dans laquelle les valeurs négatives sont dans l'ordre inverse par rapport à leurs interprétations entière non signées.