10
votes

Signe d'un numéro de point flottant

Y a-t-il un moyen facile de déterminer le signe d'un numéro de point flottant?

J'ai expérimenté et proposé cela: xxx

où (c [3] & 0x10000000) donne une valeur> 0 pour un nombre négatif, mais je pense que cela me demande de faire les hypothèses qui:

  • Les octets de la machine sont de 8 bits BIG
  • Un numéro de point flottant est 4 octets BIG?
  • le bit le plus significatif de la machine est le plus gauche (endiangité?)

    Merci de me corriger si l'une de ces hypothèses est fausse ou si je vous ai manqué.


8 commentaires

Jusqu'à ce que nous sachions quel système vous êtes sur vous, comment pouvons-nous corriger votre hypothèse de savoir si votre machine est grande ou petite endienne?


Mes hypothèses ont été généralisées, désolées si ce n'était pas clair.


Quel est le problème avec <0? Je sais même Point flottant 0 peut avoir un signe, mais le simple


@HHAFEZ parce que les seuls résultats que je reçois sont -0 et +0


OH OK, alors faites une simple valeur == -0.0 sinon utilisez le moins que l'opérateur simple. Je sais que c'est un exercice intéressant qui tente d'obtenir le piqué de signalisation, mais si tout ce que vous voulez, c'est <0 ou == -0.0 alors c'est assez simple alors :)


Ce n'est pas possible car -0.0f == 0.0f


Voir ma modification à James Réponse, je l'ai testé et distingue entre 0.0f et -0.0F de manière très simple.


Le code donné dans vos deux modifications ne fonctionne pas sur ma machine.


9 Réponses :


10
votes

En supposant que c'est un numéro de point flottant valide (et non, par exemple, NaN):

float f;
bool is_negative = f < 0;


41 commentaires

@Rarge: Que faites-vous dans lequel cela compte? Demandez à l'objectif, pas à l'étape.


@Rarge: Pourquoi vous souciez-vous de savoir si un numéro est positif ou négatif zéro? Dans tous les cas, le nombre zéro est une valeur unique qui a deux représentations dans certains formats de points flottants. Ce code teste le signe d'un numéro; Cela ne teste pas une représentation particulière du nombre, cependant.


Je calcule le produit DOT perp de deux vecteurs pour déterminer le moyen de faire pivoter un objet. Lorsque le numéro est -0, je dois faire pivoter un moyen, quand il est 0, je dois le faire pivoter un autre.


@Rarge: Êtes-vous sûr que le nombre ne sait pas normalisé après des calculs?


@Vlad oui, si je imprime f il donne -0


@Rarge: Si vous comptez pour pouvoir distinguer entre +0 et -0 après un calcul du produit de point, serait-il juste de dire que vous avez un algorithme hautement instable?


@Rarge: Serez-vous capable de remarquer la différence? Zéro est zéro, quelle que soit votre façon de faire pivoter la matière?


@Gman La direction de rotation est importante comme une solution est plus courte que l'autre


@Rarge: Quoi? Il n'y a pas de plus court entre zéro et "zéro négatif". Comme je l'ai demandé, comment voudriez-vous même remarquer? Ils sont le même nombre . La direction est arbitraire entre 0 et "-0".


@Gman Je ne peux pas expliquer les raisons sur la raison pour laquelle l'ordinateur me donne -0 et +0 de mon arithmétique mais cela me donne -0 lorsque j'ai besoin de faire pivoter un moyen et +0 lorsque j'ai besoin de faire pivoter l'autre. Que les chiffres soient ou non les mêmes mathématiquement n'a pas d'importance.


@Gman: Pour être juste, -0 peut arriver à la suite d'un nombre négatif fini qui a ensuite été arrondi vers zéro, auquel cas une différence sémantique. Malgré tout, j'ai du mal à imaginer comment cela pourrait être important dans la situation de l'OP; Il est probable qu'il y aura de loin plus grandes sources d'erreur qui domineront ce résultat.


Je voudrais un peu de voir ce code qui calcule les produits DOT: - / Cela ne sonne pas du tout.


@Rarge: voulez-vous dire que les résultats possibles pour votre calcul sont -0 et +0? Cela semble juste mal.


J'ai un vecteur avancé et un vecteur de destination. Je calcule le produit DOT perp de ce type comme: avancement.x * dest.y - upwwe.y * dort.x; Lorsque le résultat est -0, j'ai besoin de le faire pivoter dans le sens des aiguilles d'une montre lorsqu'il est +0, j'ai besoin de le faire pivoter dans le sens anti-horaire.


@Rarge: Encore une fois: sont les seuls résultats possibles -0 et +0? Dans tous les cas, votre problème est mieux résolu de stocker des rotations un angle au lieu d'un vecteur, puis d'interpoler l'angle.


@Rarge: La seule situation où le produit ponctuel perpendiculaire donnera 0 est lorsque les deux vecteurs sont parallèles (ou de manière aussi proche de l'être, mais indiscernables compte tenu des limitations de mathématiques à virgule flottante). Il n'est pas raisonnable de s'attendre à la différence entre +0 et -0 pour être significatif dans cette situation.


Il donne +0 et -0 pour tous les angles (avec des étapes de 1 degré) entre 0 et 359 degrés. La seule explication est les limitations de points flottants. Mais cela me donne toujours -0 -0 quand j'ai besoin d'aller un moyen et +0 quand j'ai besoin d'aller l'autre.


Après avoir fait des expériences, le point à laquelle le signe des panneaux est l'endroit où il passe le point à laquelle les deux vecteurs sont parallèles.


@Rarge: Votre algorithme pour calculer le produit DOT est cassé s'il renvoie zéro pour deux vecteurs ...


@Rarge: Ensuite, vous avez des vecteurs dont les valeurs de composants sont proches de l'epsilon à virgule flottante (auquel cas le résultat sera proche de la signification), ou si vous avez un bug! Le résultat d'un produit DOT ne doit pas, dans le cas général, être zéro!


@James: Si les vecteurs sont Minuscule , le résultat de la multiplication croisée pourrait déborder. Bien entendu, la solution consiste à normaliser d'abord les vecteurs.


Échange de la place des vecteurs en somme: E.G. Dest.x * Forward.y - Dest.y * Forward.x me donne toujours 0. Les deux vecteurs sont normalisés.


@Oli: Oui, mais l'OP a dit que pour toutes les intrants, le résultat est égal à zéro, ce qui n'a pas de sens du tout. (Bien que j'ai peut-être mal compris le commentaire de l'OP.)


William Kahan's Paper "Comment le point flottant de Java fait mal à tout le monde de partout" inclut un certain caractère convaincant Exemples (par exemple de la mécanique des fluides) qui démontrent pourquoi il est important de distinguer entre +0,0 et -0,0. En un mot, cela concerne des fonctions complexes, des coupures de branche et des identités qui ne parviennent que si +0,0 et -0,0 sont distinguées.


@Rarge: alors quelque chose est sérieusement faux. Vous ne devriez pas obtenir 0 pour autre chose que des angles infinisimallysimalement à zéro (dans les limites du point flottant).


@James quand j'ai dit toutes les valeurs, je voulais dire faire tourner mon vecteur 1 degré, puis trouver le produit DOT perp, répétant que jusqu'à ce que l'angle entre les deux vecteurs soit ~ 0


@JIM: dans l'ésotérique (et cela ne doit pas les minimiser) des cas, oui, cela compte. Prendre le produit DOT de deux vecteurs n'est pas un de ces cas.


Je comprends qu'il doit y avoir un problème avec mon code quelque part pour qu'il soit toujours produire ± 0 et je rechercherai donc une solution différente, mais il est évident que, dans certains cas, il est important de pouvoir distinguer le signe et donc je suis toujours à la recherche d'une réponse à mon problème.


@Rarge: Comme il n'ya pas de moyen de le faire dans la bibliothèque standard C ++ (au moins, pas que je ne connaisse pas, et non que quiconque a démontré ici), vous pouvez envisager d'utiliser des fonctionnalités fournies par votre plate-forme, par exemple, par exemple, La bibliothèque d'exécution Visual C ++ C inclut _COPYSIGN .


@Oli: a accepté - mais, chemin utile, la question a été posée: "Pourquoi vous souciez-vous de savoir si un nombre est positif ou négatif zéro?". Je pensais qu'il valait la peine de mentionner la prise de Kahan sur elle, surtout à la lumière de son rôle dans le développement de la norme IEEE-754.


Comment la condition (f == -0.0 || f <0) différent de ou meilleur que (f <= 0,0) ?


J'ai trouvé le problème avec mon code pour les personnes intéressées. Je prenais le produit Dot Perp entre le mauvais axe, je le voulais entre x et z, pas x et y. Dans mon cas, la valeur y a toujours été 0 pour les deux vecteurs, il était donc efficacement possible de 0 - 0 Les résultats ouvrent les yeux vers un arithmétique à virgule flottante et la façon dont 0 - 0 peut être ± 0 et comment les erreurs triviales peuvent être trompeuses! @James je vais lire sur _COPYSIGN et sa mise en œuvre; Merci.


@ Goibe ce n'est pas. -0.0f == 0.0f et ne répond donc pas à la question initiale.


-0,0 et 0.0 est égal, mais ils ne sont pas de manière significative égale; Vous pouvez tester s'ils sont représentatifs de manière significative en réinterprétant les variables float comme des tableaux de caractères et en utilisant std :: égal pour comparer leur contenu. (Je ne suis pas sûr de savoir si float f = -0,0; est nécessaire pour donner lieu à f ayant une valeur de zéro négatif, bien que)


Qu'est-ce qui est avec toutes les modifications folles de @hhafezz? La réponse actuelle (impliquant la coulée à longue) est tout simplement fausse.


@Mark: oh mec, merci pour la tête de tête; Avec tous les commentaires à cette question montrant sur ma page de réception, j'ai totalement manqué les modifications: - | J'ai retourné les changements non désirés.


@JIM: Droite, j'ai demandé que, même si je ne voulais pas dire que cela n'a jamais été nécessaire de différencier le zéro positif et négatif, je voulais juste savoir ce que le cas d'utilisation de l'OP était pour cela afin que je puisse mieux résoudre la question (Si l'OP avait eu un usage réel pour les différencier, j'aurais laissé quelqu'un d'autre répondre à la question ... Maths avancés n'est pas ma chose :-d). Merci pour le lien; J'ai écrémé et je l'ai trouvé intéressant.


@Mark Dickinson: La condition (f == -0.0 || f <0) est true lorsque f == + 0,0 . Ce n'était pas destiné non plus. Le problème fondamental est que f == - f lorsque f est zéro.


@Msalters: Oui, c'est exactement ce que j'essayais de souligner. Mon commentaire aurait dû être écrit un peu plus clairement; Pardon. :-) (Cela me ressemblait au moment de ce commentaire comme si James Mcnellis proposait de la condition (f == -0.0 || f <0) en tant que solution; J'ai plus tard compris qu'il n'était pas: il y en avait édition de voyous en cours.)


Un exemple où cette distinction est importante dans les tests de rayons AABB. La division par les composants de la direction de rayons échangera votre intervalle intersecté en fonction de + -0


Je vois que l'OP a trouvé son bogue, mais pour répondre à la question initiale. Si le système cible n'a pas de copysignien () ou similaire, et PrintF () indique -0,0; Je sais que c'est laid (et éventuellement non portable) mais que diriez-vous: Sprintf (BUF, "% F", VAL); Si (BUF [0] == '-');



11
votes

Utiliser Signbit () de Math.H.


2 commentaires

Signbit () ne fait pas partie de la bibliothèque standard C ++ actuelle. Il fait partie de la bibliothèque de standard C ++ 0x proposée (la plupart - toutes? - de la bibliothèque standard C99 fait partie de C ++ 0x).


@Jamesmcnellis maintenant en C ++ 11, en.cppreference.com/w/cppp / Numérique / Math / Signbit



-1
votes

Pourquoi pas si (f <0,0) ?


5 commentaires

Parce que les nombres de points flottants ont deux zéros -0.0f et + 0.0f


@Rarge: J'échoque le commentaire de Gman à @James McNellis.


Cela ne fournit pas de réponse à la question. Pour critiquer ou demander des éclaircissements d'un auteur, laissez un commentaire sous leur poste. - de l'avis


@WILL - Ceci est essentiellement identique au coquelicot de la réponse acceptée, alors je dirais que cela fait répond à la question (bien que cela soit il y a 6 ans, cela fait donc un certain temps.) Cela a à voir avec "Critique" ou "Clarification"? Commentaire généré automatiquement égaré?


C'est l'une des options de la file d'attente de révision - cela ressemble plus à un commentaire qu'une réponse complète. Mais oui, ça fait juste l'ignorer. La réponse acceptée l'explique un peu mieux. Des réponses à une ligne courtes pourraient aussi bien être des commentaires.



11
votes

Essayez xxx

de

Une autre chose utile peut être # comprenant , s'il est disponible sur votre système / compilateur.


24 commentaires

Copysign () ne fait pas partie de la bibliothèque standard C ++ en cours. Il fait partie de la bibliothèque de standard C ++ 0x proposée (la plupart - toutes? - de la bibliothèque standard C99 fait partie de C ++ 0x).


@Vlad: Cela ne fait pas par magie la partie de la norme actuelle.


@Vlad: Un autre compilateur C ++ largement utilisé, Visual C ++, n'inclut pas Copysign () dans sa mise en œuvre de la bibliothèque standard (et il ne doit pas nécessairement, au moins pas avant le temps après C ++ 0x est fini).


Oui, mais en utilisant cette fonction, si disponible, peut être utile à l'OP.


@Vlad: Cela vient de déplacer le but de but de "c'est certainement utilisable" de "cela pourrait être utilisable". Veuillez vous tenir à l'argument original que @James présentés, qui était "ce n'est pas standard", que vous avez échoué à réfuter. Soyez honnête, acceptez cela, puis passez à autre chose. N'essayez pas d'essayer de le pousser sous le tapis.


@Gman: C'est pourquoi je dis "essayer" et non "Vous pouvez y parvenir en utilisant". Bien qu'il soit non standard, il est soutenu par certains des compilateurs largement utilisés, peut donc être utile pour l'OP. Nous sommes ici pour aider à résoudre les problèmes, à ne pas faire respecter les normes, non?


J'utilise Visual C ++ et je ne peux donc pas l'utiliser. Il peut être bénéfique de voir le fonctionnement intérieur de la fonction cependant.


@Vlad: Déprétable. Je préfère donner des solutions standard, propres et bien définies aux problèmes que des solutions non portables et des travaux de conjecture.


@Rarge: Vous voudrez peut-être jeter un coup d'œil à << a href = "http://www.sc.stanford.edu/histar/src/pkg/histar/incluce/eeee754.h" rel = "NOFOOL NOREFERRER"> SCS.STANFORD.EDU/HISTAR/SRC/PKG/ULBIBC/Include/eeee754.h >


@Gman: Je suis entièrement d'accord avec votre dernier commentaire. Cependant, que faire si une solution compatible standard n'est pas disponible?


@Vlad: Ensuite, vous l'enveloppez et traitez-la comme si c'était. Ce n'est pas un tel cas.


@Gman: Signbit / Copysign sont standard, propres et bien définis. Ils ne sont également pas pris en charge par un compilateur particulier non mentionné avant que cette réponse soit affichée.


@Rarge: Désolé, extra > dans mon dernier lien


@Gman: Vous le dites comme s'il y avait une meilleure solution présentée, permettant de distinguer entre + 0.0f et -0.0f . Ma proposition fonctionne correctement au moins sur la GCC actuelle.


@Fred: Où dans la norme? @Vlad: Le problème n'a pas besoin de la fonctionnalité de cette réponse, elle a besoin de la possibilité de réparer son code. S'il réellement finit par avoir besoin de cela, j'écrirai la fonction. Comme vous dites, résolvons le problème; Dans ce cas, l'OP est égaré.


@Gman: J'espère que l'OP dit qu'il a besoin de connaître le signe.


@Vlad: ah. Au fil du temps, vous apprendrez quand les gens posent des questions, plus souvent, ils ne savent pas vraiment quelles mesures ils doivent prendre. (Après tout, s'ils le faisaient, ils n'auraient pas le problème.) Cela conduit à la ligne directrice, Demandez au but, pas le pas . Cela permet aux réponses indiquer au questionneur les étapes dont ils ont besoin pour résoudre le problème, qui peut ou non être les étapes à laquelle l'Asker pensait. L'OP ne l'a pas fait, alors je ne fais pas confiance, il a besoin de cette étape sans autre argument; et cela n'a pas été fourni.


Bien entendu, cela est soutenu par votre déclaration même "Nous sommes ici pour aider à résoudre les problèmes". Je dis "Exactement, alors en fait Découvrez quel est le problème, au lieu de faire une hypothèse assez basse, l'OP sait qu'il doit prendre cette étape." Celles-ci sont bien sûr des directives. Parfois, les gens savent exactement ce qu'ils doivent faire et ne savent tout simplement pas comment le faire. Mais ce n'est pas quelque chose que vous devriez supposer si vous voulez vraiment les aider.


@Gman: discutable :-) Nous pouvons aussi bien aller de l'avant et demander si OP veut vraiment écrire un programme. Il devrait y avoir une ligne où nous nous arrêtons dans nos hypothèses. Quoi qu'il en soit, différentes hypothèses produisent des réponses différentes, plus c'est le meilleur.


@Vlad: Je n'ai pas dit que nous devons nécessairement interroger tout ce que l'OP essaie de faire. Mais c'est une bonne idée d'être sceptique quant au problème prima facie et de remettre en question la justification derrière elle. @Fred: Eh bien?


@Gman: Oui, mais nous pourrions demander par exemple si l'OP a besoin du produit DOT du tout (j'ai tiré des commentaires à l'autre réponse que le produit DOT est ce qui est calculé), ou s'il doit en effet calculer la direction, ou si les graphismes sont les moyens de représentation appropriés de ses informations. La ligne frontière (qui détermine ce que nous acceptons et ce que ce n'est pas) est vague. ERGO: L'aide sur toute la profondeur possible est bonne et précieuse.


@GMAN: Si vous ne trouvez honnêtement pas où ils sont définis, je connais peut-être un bon site de questions-réponses que vous pouvez poser cette question. Si vous ne faites que traîner, bien, honte à vous et honte à moi pour répondre.


@Fred: sérieusement? Que diable? Je pose une question simple et vous m'abuse de la traîne. Faites-vous toujours ça? Non, je ne traîne pas. Et je ne vais pas poser à une question de quelque chose que vous pourriez trivialement donner une réponse dans une minute. Alors s'il vous plaît: où sont-ils définis dans la norme? Vous avez dit qu'ils sont là, où?


@Fred: Bump. Veuillez répondre à la question, j'aimerais savoir où.



2
votes

1) Tailleof (int) n'a rien à voir avec ça.

2) en supposant que char_bit == 8, oui.

3) Nous avons besoin de MSB pour cela, mais l'endansnité n'affecte que l'ordre d'octet, pas l'ordre de bit, donc le bit que nous devons vérifier est c [0] & 0x80 pour grand endianness ou c [3] & 0x80 Pour peu, il serait donc préférable de déclarer Union avec un uint32_t et de vérifier avec 0x80000000.

Cet astuce n'a de sens que pour les opérandes de mémoire non spéciales. Le faire à un float dans le registre XMM ou X87 sera plus lent que l'approche directe. En outre, il ne traite pas les valeurs spéciales telles que NAN ou INF.


0 commentaires

1
votes

Google le format de point flottant pour votre système. Beaucoup utilisent IEEE 754 et il existe un bit de signature spécifique dans les données à examiner. 1 est négatif 0 est positif. D'autres formats ont quelque chose de similaire et aussi facile à examiner.

Remarque essayer d'obtenir le compilateur pour vous donner exactement le numéro souhaité avec une attribution codée dure comme F = -0.0F; peut ne pas fonctionner. N'a rien à voir avec le format de point flottant, mais a à voir avec l'analyseur et la bibliothèque C / C ++ utilisée par le compilateur. Générer un moins zéro peut être ou ne pas être ce trivial en général.


0 commentaires

0
votes

0 commentaires

1
votes

venir à cette fin, mais j'ai pensé à une autre approche.

Si vous savez que votre système utilise le format de point flottant IEEE754, mais pas à quel point les types de points flottants sont relatifs aux types d'entier, vous pouvez faire quelque chose comme ceci: xxx

essentiellement, vous traitez les octets de votre float en tant que type entier non signé, puis passez à droite tout sauf l'un des bits (le bit des signes) hors de existence. '>>' fonctionne indépendamment de l'endansnicité, donc cela contourne ce problème.

S'il est possible de déterminer la pré-exécution du type entier non signé a la même longueur que le type de point flottant, vous pouvez abréger ceci: xxx

cela a fonctionné sur mes systèmes de test; Toute personne voit des mises en garde ou négligée 'GOTCHAS'?


1 commentaires

Je pense flotter d = f; est redondant en raison de l'opérateur >> n'est pas mutable pour ses opérandes, autres que cela va bien. Je suis arrivé à la même solution supposant que mon flotteur a la même longueur que non signé (4 octets)



0
votes
(int)(x > 0) - (int)(x < 0);

0 commentaires