What's minus zero in floating point?
Idea: In the next edition, stop accepting `0.` as a valid float literal - Rust Internals
numerical analysis - How to represent zero as floating point number? - Computer Science Stack Exchange
Need a quick breakdown of meanings of float, int, and how to use them
How can 2 be represented as an exact number? 4? 15? 0.5? The answer is just that some numbers can be represented exactly in the floating-point format (which is based on base-2/binary) and others can't.
This is no different from in decimal. You can't represent 1/3 exactly in decimal, but that doesn't mean you can't represent 0.
Zero is special in a way, because (like the other real numbers) it's more trivial to prove this property than for some arbitrary fractional number. But that's about it.
So:
what is it about these values (0, 1/16, 1/2048, ...) that allows them to be represented exactly.
Simple mathematics. In any given base, in the sort of representation we're talking about, some numbers can be written out with a fixed number of decimal places; others can't. That's it.
You can play online with H. Schmidt's IEEE-754 Floating Point Converter for different numbers to see a bunch of different representations, and what errors come about as a result of encoding into those representations. For starters, try 0.5, 0.2 and 0.1.
It was my (perhaps naive) understanding that all floating point values contained some instability.
No, absolutely not.
You want to treat every floating point value in your program as potentially having some small error on it, because you generally don't know what sequence of calculations led to it. You can't trust it, in general. I expect someone half-taught this to you in the past, and that's what led to your misunderstanding.
But, if you do know the error (or lack thereof) involved at each step in the creation of the value (e.g. "all I've done is initialised it to zero"), then that's fine! No need to worry about it then.
Here is one way to look at the situation: with 64 bits to store a number, there are 2^64 bit patterns. Some of these are "not-a-number" representations, but most of the 2^64 patterns represent numbers. The number that is represented is represented exactly, with no error. This might seem strange after learning about floating point math; a caveat lurks ahead.
However, as huge as 2^64 is, there are infinitely many more real numbers. When a calculation produces a non-integer result, the odds are pretty good that the answer will not be a number represented by one of the 2^64 patterns. There are exceptions. For example, 1/2 is represented by one of the patterns. If you store 0.5 in a floating point variable, it will actually store 0.5. Let's try that for other single-digit denominators. (Note: I am writing fractions for their expressive power; I do not intend integer arithmetic.)
1/1– stored exactly1/2– stored exactly1/3– not stored exactly1/4– stored exactly1/5– not stored exactly1/6– not stored exactly1/7– not stored exactly1/8– stored exactly1/9– not stored exactly
So with these simple examples, over half are not stored exactly. When you get into more complicated calculations, any one piece of the calculation can throw you off the islands of exact representation. Do you see why the general rule of thumb is that floating point values are not exact? It is incredibly easy to fall into that realm. It is possible to avoid it, but don't count on it.
Some numbers can be represented exactly by a floating point value. Most cannot.
I found that some floating point data types uses -0, but am not sure how it works. I am looking for some blog or link for a clear explanation on this topic.
I am writing code that will only run on modern, personal computers, not anything embedded. So I believe that 1 and 0 will always be represent able within the floating point system. Is this a fair assumption? Is it ok in this case to use if (x == 1) and if (x == 0), where x is a float?
Edit: I'm sorry, I forgot to add that x is manually set to 0 or 1, not computed.
It's a compound literal.
foo(&(float){0})
is a shorthand for
float x = 0;
foo(&x);
The only difference between those is that with a compound literal it's impossible to access the number after the call (which matters if foo changes it).
It is used to pass the constant expression to the function which expects the reference.
Example
float sq(float *f)
{
*f = *f * *f;
return *f;
}
int main(void)
{
printf("%f\n", sq(&(float){3.0f}));
}