Sign of a floating point number

35,192

Solution 1

Assuming it's a valid floating point number (and not, for example, NaN):

float f;
bool is_negative = f < 0;

It is left as an exercise to the reader to figure out how to test whether a floating point number is positive.

Solution 2

Try

float s = copysign(1, f);

from <math.h>

Another helpful thing may be #including <ieee754.h>, if it's available on your system/compiler.

Solution 3

Use signbit() from math.h.

Solution 4

1) sizeof(int) has nothing to do with it.

2) assuming CHAR_BIT == 8, yes.

3) we need MSB for that, but endianness affects only byte order, not bit order, so the bit we need to check is c[0]&0x80 for big endianness, or c[3]&0x80 for little, so it would be better to declare union with an uint32_t and checking with 0x80000000.

This trick have sense only for non-special memory operands. Doing it to a float value that is in XMM or x87 register will be slower than direct approach. Also, it doesn't treat the special values like NaN or INF.

Solution 5

google the floating point format for your system. Many use IEEE 754 and there is specific sign bit in the data to examine. 1 is negative 0 is positive. Other formats have something similar, and as easy to examine.

Note trying to get the compiler to exactly give you the number you want with a hard coded assignment like f = -0.0F; may not work. has nothing to do with the floating point format but has to do with the parser and the C/C++ library used by the compiler. Generating a minus zero may or may not be that trivial in general.

Share:
35,192
Rarge
Author by

Rarge

Updated on March 30, 2021

Comments

  • Rarge
    Rarge about 3 years

    Is there an easy way to determine the sign of a floating point number?

    I experimented and came up with this:

    #include <iostream>
    
    int main(int argc, char** argv)
    {
     union
     {
      float f;
      char c[4];
     };
    
     f = -0.0f;
     std::cout << (c[3] & 0x10000000) << "\n";
    
     std::cin.ignore();
     std::cin.get();
     return 0;
    }
    

    where (c[3] & 0x10000000) gives a value > 0 for a negative number but I think this requires me to make the assumptions that:

    • The machine's bytes are 8 bits big
    • a float point number is 4 bytes big?
    • the machine's most significant bit is the left-most bit (endianness?)

    Please correct me if any of those assumptions are wrong or if I have missed any.

  • Rarge
    Rarge over 13 years
    Because floating point numbers have two zeros -0.0f and +0.0f
  • GManNickG
    GManNickG over 13 years
    @Rarge: What are you doing in which that matters? Ask the goal, not the step.
  • Oliver Charlesworth
    Oliver Charlesworth over 13 years
    @Rarge: I echo @GMan's comment to @James McNellis's answer.
  • James McNellis
    James McNellis over 13 years
    @Rarge: Why do you care whether a number is positive or negative zero? In any case, the number zero is a single value that has two representations in some floating point formats. This code does test the sign of a number; it does not test for a particular representation of the number, though.
  • Rarge
    Rarge over 13 years
    I am calculating the perp dot product of two vectors to determine which way to rotate an object. When the number is -0 I must rotate one way, when it is 0 I must rotate it another.
  • Vlad
    Vlad over 13 years
    @Rarge: are you sure that the number does not get normalized after calculations?
  • Rarge
    Rarge over 13 years
    @Vlad Yes, if I print f it gives -0
  • Oliver Charlesworth
    Oliver Charlesworth over 13 years
    @Rarge: If you're relying on being able to distinguish between +0 and -0 after a dot-product calculation, would it be fair to say that you have a highly-unstable algorithm?
  • GManNickG
    GManNickG over 13 years
    @Rarge: Will you be able to notice the difference? Zero is zero, will which way you rotate matter?
  • James McNellis
    James McNellis over 13 years
    signbit() is not part of the current C++ Standard Library. It is part of the proposed C++0x Standard Library (most--all?--of the C99 Standard Library is part of C++0x).
  • Rarge
    Rarge over 13 years
    @GMan The direction of rotation is important as one way is shorter than the other
  • James McNellis
    James McNellis over 13 years
    copysign() is not part of the current C++ Standard Library. It is part of the proposed C++0x Standard Library (most--all?--of the C99 Standard Library is part of C++0x).
  • GManNickG
    GManNickG over 13 years
    @Rarge: What? There is no shorter between zero and "negative zero". Like I asked, how would you even notice? They are the same number. The direction is arbitrary between 0 and "-0".
  • GManNickG
    GManNickG over 13 years
    @Vlad: That doesn't magically make it part of the current standard.
  • Rarge
    Rarge over 13 years
    @GMan I cannot explain the reasons as to why the computer is giving me -0 and +0 from my arithmetic but it gives me -0 when I need to rotate one way and +0 when I need to rotate the other. Whether or not the numbers are the same mathematically does not matter.
  • Oliver Charlesworth
    Oliver Charlesworth over 13 years
    @GMan: To be fair, -0 can come as the result of some finite negative number that was subsequently rounded toward zero, in which case, there is a semantic difference. Even so, I have trouble imagining how this could be significant in the OP's situation; it's likely that there will be far greater sources of error that will dominate this result.
  • James McNellis
    James McNellis over 13 years
    @Vlad: Another widely used C++ compiler, Visual C++, does not include copysign() in its Standard Library implementation (and it doesn't have to, at least not until some time after C++0x is finished).
  • Vlad
    Vlad over 13 years
    Yes, but using this function, if available, may be helpful to the OP.
  • James McNellis
    James McNellis over 13 years
    I'd kind of like to see this code that computes dot products :-/ This doesn't sound right at all.
  • GManNickG
    GManNickG over 13 years
    @Rarge: Do you mean the only possible results for your calculation are -0 and +0? That just sounds wrong.
  • GManNickG
    GManNickG over 13 years
    @Vlad: That's just moving the goalpost from "It's definitely usable" to "It might be usable". Please stick to the original argument that @James presented, which was "it's not standard", which you failed to refute. Be honest, accept that, and then move on. Don't just try to push it under the rug.
  • Vlad
    Vlad over 13 years
    @GMan: that's why I say "try" and not "you can achieve this using". Despite of being non-standard, it's supported by some of the widely used compilers, therefore may be helpful to the OP. We are here to help to solve the problems, not to enforce the standards, right?
  • Rarge
    Rarge over 13 years
    I have a forward vector and a destination vector. I calculate the perp dot product like so: forward.x * dest.y - forward.y * dest.x; When the result is -0 I need to rotate it clockwise when it is +0 I need to rotate it anti-clockwise.
  • Rarge
    Rarge over 13 years
    I am using Visual C++ and therefore cannot use this. It may be beneficial to see the inner workings of the function though.
  • GManNickG
    GManNickG over 13 years
    @Vlad: Debatable. I'd rather give standard, clean, well-defined solutions to problems than non-portable solutions and guess-work.
  • Vlad
    Vlad over 13 years
    @Rarge: you might want to have a look at <scs.stanford.edu/histar/src/pkg/uclibc/include/ieee754.h>
  • Vlad
    Vlad over 13 years
    @GMan: I fully agree with your last comment. However, what to do if a standard-compliant solution is not available?
  • GManNickG
    GManNickG over 13 years
    @Vlad: Then you wrap it up and treat it like it is. This isn't such a case.
  • GManNickG
    GManNickG over 13 years
    @Rarge: Again: Are the only possible results -0 and +0? In any case, your problem is better solved be storing rotations an angle instead of a vector, and then just interpolating the angle.
  • Fred Nurk
    Fred Nurk over 13 years
    @GMan: signbit/copysign are standard, clean, and well-defined. They are also not supported by one particular compiler not mentioned before this answer was posted.
  • Vlad
    Vlad over 13 years
    @Rarge: sorry, extra > in my last link
  • Oliver Charlesworth
    Oliver Charlesworth over 13 years
    @Rarge: The only situation where the perpendicular dot product will give 0 is when the two vectors are parallel (or so close as to be all but indistinguishable given the limitations of floating-point maths). It is not reasonable to expect the difference between +0 and -0 to be meaningful in this situation.
  • Vlad
    Vlad over 13 years
    @GMan: you say it as if there were a better solution presented, allowing to distinguish between +0.0f and -0.0f. My proposal works correctly at least on current gcc.
  • Rarge
    Rarge over 13 years
    It is giving +0 and -0 for all angles (with steps of 1 degree) between 0 and 359 degrees. The only explanation is the float point limitations. But it is still giving me -0 when I need to go one way and +0 when I need to go the other.
  • Rarge
    Rarge over 13 years
    After doing some experimenting, the point at which the sign switches is where it passes the point at which the two vectors are parallel.
  • James McNellis
    James McNellis over 13 years
    @Rarge: Your algorithm to compute the dot product is broken if it returns zero for any two vectors...
  • Oliver Charlesworth
    Oliver Charlesworth over 13 years
    @Rarge: Then either you have vectors whose component values are close to floating-point epsilon (in which case the result will be close to meaningless), or you have a bug! The result of a dot product should not, in the general case, be zero!
  • Oliver Charlesworth
    Oliver Charlesworth over 13 years
    @James: If the vectors are miniscule, then the result of the cross-multiplication could underflow. The solution, of course, is to normalise the vectors first.
  • Rarge
    Rarge over 13 years
    Swapping the place of the vectors in the sum: e.g. dest.x * forward.y - dest.y * forward.x always gives me 0. Both vectors are normalized.
  • James McNellis
    James McNellis over 13 years
    @Oli: Yes, but the OP said that for all inputs the result is zero, which doesn't make sense at all. (Though, perhaps I misunderstood the OP's comment.)
  • Jim Lewis
    Jim Lewis over 13 years
    William Kahan's paper "How Java's Floating Point Hurts Everyone Everywhere" includes some compelling examples (e.g. from fluid mechanics) that demonstrate why it's important to distinguish between +0.0 and -0.0. In a nutshell, it has to do with complex functions, branch cuts, and identities that fail to hold unless +0.0 and -0.0 are distinguished.
  • Oliver Charlesworth
    Oliver Charlesworth over 13 years
    @Rarge: Then something is seriously wrong. You should not be getting 0 for anything other than angles infinitesimally close to zero (within the limitations of floating-point).
  • Rarge
    Rarge over 13 years
    @James When I said all values I meant rotating my vector 1 degree then finding the perp dot product, repeating that until the angle between the two vectors is ~0
  • GManNickG
    GManNickG over 13 years
    @Fred: Where in the standard? @Vlad: The problem doesn't need the functionality this answer provides, it needs the OP to fix his code. If he actually ends up needing this, then I'll write the function. Like you say, let's solve the problem; in this case, the OP is misguided.
  • Oliver Charlesworth
    Oliver Charlesworth over 13 years
    @Jim: In esoteric (and that's not to downplay them) cases, yes, it matters. Taking the dot product of two vectors is not one of these cases.
  • Rarge
    Rarge over 13 years
    I understand that there must be a problem with my code somewhere for it to be always producing ±0 and so I will seek a different solution but it is evident that in some cases it is important to be able to distinguish the sign and so I am still seeking an answer to my problem.
  • Vlad
    Vlad over 13 years
    @GMan: I trust the OP saying he actually needs to know the sign.
  • James McNellis
    James McNellis over 13 years
    @Rarge: Since there is no way to do this in the C++ Standard Library yet (at least, not that I know of, and not that anyone else has demonstrated here), you can consider using functionality provided by your platform, for example, the Visual C++ C Run-Time Library includes _copysign.
  • Jim Lewis
    Jim Lewis over 13 years
    @Oli: Agreed -- but, way upthread, the question was asked, "Why do you care whether a number is positive or negative zero?". I thought it was worth mentioning Kahan's take on it, especially in light of his role in the development of the IEEE-754 standard.
  • GManNickG
    GManNickG over 13 years
    @Vlad: Ah. Over time you'll learn when people ask questions, more often then not they don't actually know what steps they need to take. (After all, if they did they wouldn't have the problem.) This leads to the guideline, ask the goal, not the step. This lets the answers tell the questioner the steps they need to take to solve the problem, which may or may not be the steps the asker was thinking. The OP hasn't done so, so I don't trust he needs this step without further argument; and that hasn't been provided.
  • Mark Dickinson
    Mark Dickinson over 13 years
    How is the condition (f == -0.0 || f < 0) different from or better than (f <= 0.0)?
  • GManNickG
    GManNickG over 13 years
    That, of course, is supported by your very statement "We are here to help to solve the problems". I say "Exactly, so let's actually find out what the problem is, instead of make a fairly base-less assumption the OP knows he needs to take this step." These are, of course, guidelines. Sometimes people know exactly what they need to do, and just don't know how to do it. But that's not something you should assume if you really want to help them.
  • Vlad
    Vlad over 13 years
    @GMan: Debatable :-) We can as well go ahead and ask if OP really wants to write a program. There should be a line where we stop in our assumptions. Anyway, different assumptions produce different answers, the more is the better.
  • Rarge
    Rarge over 13 years
    I found the problem with my code for those interested. I was taking the perp dot product between the wrong axis, I wanted it between x and z, not x and y. In my case the y value was always 0 for both vectors so it was effectively doing 0 - 0 the results open one's eyes towards a floating point arithmetic and how 0 - 0 can be ±0 and how trivial errors can be misleading! @James I shall read up on _copysign and it's implementation; Thank you.
  • Rarge
    Rarge over 13 years
    @Mark It is not. -0.0f == 0.0f and so neither answer the original question.
  • GManNickG
    GManNickG over 13 years
    @Vlad: I didn't say we necessarily need to question everything the OP is trying to do. But it's a good idea to be skeptical about the problem prima facie, and question the rationale behind it. @Fred: Well?
  • James McNellis
    James McNellis over 13 years
    -0.0 and 0.0 are equal, but they are not representationally equal; you can test whether they are representationally equal by reinterpreting the float variables as char arrays and using std::equal to compare their contents. (I am not sure whether float f = -0.0; is required to result in f having a value of negative zero, though)
  • Mark Dickinson
    Mark Dickinson over 13 years
    What's with all the crazy edits by @hhafez? The current answer (involving casting to long) is just plain wrong.
  • Vlad
    Vlad over 13 years
    @GMan: yes, but we could ask for example whether the OP needs dot product at all (I learned from the comments to the other answer that the dot product is what is being calculated), or whether he indeed needs to calculate the direction, or whether the graphics is the appropriate means of representation his information. The border line (that determines what we accept and what not) is vague. Ergo: help on any possible problem depth is good and valuable.
  • James McNellis
    James McNellis over 13 years
    @Mark: Oh man, thanks for the heads-up; with all the comments to this question showing up on my inbox page, I totally missed the edits :-| I've reverted the unwanted changes.
  • Fred Nurk
    Fred Nurk over 13 years
    @GMan: If you honestly can't find where they are defined, I might know a good Q&A site you can ask that question. If you're just trolling, well, shame on you, and shame on me for responding.
  • James McNellis
    James McNellis over 13 years
    @Jim: Right, I asked that, though I didn't mean to imply that it was never necessary to differentiate between positive and negative zero, I just wanted to know what the OP's use case was for this so I could better address the question (had the OP had a real use for differentiating them, I'd have let someone else answer the question... advanced math isn't my thing :-D). Thanks for the link; I skimmed through it and found it interesting.
  • GManNickG
    GManNickG over 13 years
    @Fred: Seriously? What the hell? I ask a simple question and you acuse me of trolling. Do you always do that? No, I'm not trolling. And I'm not going to ask a question for something that you could trivially give an answer to in a minute. So please: where are they defined in the standard? You said they are there, where?
  • GManNickG
    GManNickG over 13 years
    @Fred: Bump. Please answer the question, I'd like to know where.
  • MSalters
    MSalters over 13 years
    @Mark Dickinson: The condition (f == -0.0 || f < 0) is true when f==+0.0. That wasn't intended either. The fundamental problem is that f==-f when f is zero.
  • Mark Dickinson
    Mark Dickinson over 13 years
    @MSalters: Yes, that's exactly what I was trying to point out. My comment should have been written a bit more clearly; sorry. :-) (It looked to me at the time of that comment as though James McNellis was proposing the condition (f == -0.0 || f < 0) as a solution; I later realised that he wasn't: there was some rogue editing going on.)
  • Will
    Will over 8 years
    This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. - From Review
  • Oliver Charlesworth
    Oliver Charlesworth over 8 years
    @Will - This is essentially identical to the crux of the accepted answer, so I'd argue that it does answer the question (although this is 6 years ago, so it's been a while.) What does this have to do with "critique" or "clarification"? Misplaced auto-generated comment?
  • Will
    Will over 8 years
    It's one of the options in the review queue--this is more like a comment than a complete answer. But yeah, just ignore it. The accepted answer explains it a bit better. Short one-line answers might as well be comments.
  • Eelco Hoogendoorn
    Eelco Hoogendoorn almost 7 years
    An example where this distinction matters is in aabb-ray tests. Dividing by the ray direction components will swap your intersected interval depending on +-0
  • Don Mclachlan
    Don Mclachlan about 6 years
    I see the OP found his bug, but to answer the original question. If the target system doesn't have copysign() or similar, and printf() shows -0.0; I know it is ugly (and possibly non portable) but how about: sprintf(buf, "%f", val); if(buf[0] == '-');
  • Maxim Egorushkin
    Maxim Egorushkin almost 6 years
    @JamesMcNellis Now in C++11, en.cppreference.com/w/cpp/numeric/math/signbit
  • Anton Dyachenko
    Anton Dyachenko over 3 years
    I think float d = f; is redundant due to operator >> is not mutable for its operands, other than that is fine. I have come up to the same solution assuming my float has the same length as unsigned (4 bytes)