21

Hello I'm learning Objective C and I was doing the classic Calculator example.

Problem is that I'm getting a negative zero when I multiply zero by any negative number, and I put the result into a (double) type!

To see what was going on, I played with the debugger and this is what I got:

(gdb) print -2*0
$1 = 0

(gdb) print (double) -2 * 0
$2 = -0

In the second case when I cast it to a double type, it turns into negative zero! How can I fix that in my application? I need to work with doubles. How can I fix the result so I get a zero when the result should be zero?

Community
  • 1
  • 1
Guz
  • 213
  • 1
  • 2
  • 4
  • 15
    The result is correct. What's wrong with negative zero? – kennytm Mar 11 '12 at 19:08
  • 1
    Presumably your application doesn't run in GDB. Surely you will control the formatting of your output. Also, this question: "How can I fix the result so I get a zero when the result should be zero?" is based on an incorrect premise--you *did* get a zero. – Stephen Canon Mar 11 '12 at 19:12
  • 2
    Thankfully, `-0.0 == +0.0`, so your question evaporates. – Kerrek SB Mar 11 '12 at 19:52
  • 2
    I find all of the above comments non-responsive and Aram's answer as well. I am assuming this is a glitch in that the sign bit of a otherwise zero double acts differently than a normal zero, when they in fact represent the same quantity and should be displayed the same way. – Jiminion Jun 22 '17 at 18:01
  • 2
    @Jiminion It is not a glitch. -0.0 acts differently than +0.0 in [select cases](https://stackoverflow.com/q/25332133/2410359) - `atan2()` is a common case. Although they represent the same _value_, aspects of -0.0 are implementation dependent (or not if following IEEE) standards. – chux - Reinstate Monica Jun 22 '17 at 19:33
  • It is a glitch in the case the OP is running into. I have faith that IEEE standard had its reasons for doing what it did. – Jiminion Jun 22 '17 at 20:23
  • 1
    @Jiminion: the IEEE floating point specification is **very** respectable work. Everything in it was carefully crafted and it is really too bad that some C compilers do not fully implement all of its details and provisions. The main difference between `0.0` and `-0.0` is their behavior as quotient values: `1 / 0.0` is a positive infinity whereas `1 / -0.0` is a **negative** infinity, a vastly different thing. – chqrlie Jun 22 '17 at 20:38
  • 1
    Guz, Note that with `double x = -0.0000001; printf("%f\n", x);` also prints a "negative zero"-like output with `-0.000000`. Of course code could have been `printf("%e\n", x);` to see `-1.000000e-07`. – chux - Reinstate Monica Jun 22 '17 at 21:40
  • Possible duplicate of [how to make negative numbers into positive](https://stackoverflow.com/questions/4745617/how-to-make-negative-numbers-into-positive) – Ciro Santilli OurBigBook.com Nov 08 '18 at 08:33

6 Answers6

19

I did a simple test:

double d = (double) -2.0 * 0;

if (d < 0)
    printf("d is less than zero\n");
if (d == 0)
    printf("d is equal to zero\n");
if (d > 0)
    printf("d is greater than zero\n");

printf("d is: %lf\n", d);

It outputs:

d is equal to zero
d is: -0.000000

So, to fix this, you can add a simple if-check to your application:

if (d == 0) d = 0;
Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
  • 8
    Or use [signbit()](http://pubs.opengroup.org/onlinepubs/9699919799/functions/signbit.html) if you have a C99 implementation ( `if (signbit(d)) d *= -1;` ). – pmg Mar 11 '12 at 19:32
  • 3
    `if (d == 0) d = 0;` ... doesn't always work, at least on Visual Studio 2008. – Thomas Eding Jul 23 '13 at 16:56
  • @ThomasEding of course, non-standard compilers do non-standard things. It would appear that they are violating the IEEE standard for floating points. – Richard J. Ross III Jul 23 '13 at 17:10
  • 1
    @ThomasEding `double x = -0.0; if (x == 0.0) { x = 0.0; } char buf[100]; sprintf(buf, "d is: %lf\n", x);` and VS 2008 worked. Perhaps how you set/view `d` is in error. – chux - Reinstate Monica Jun 22 '17 at 19:22
  • 1
    This answer is all implementation-specific ; testing on your PC is no guarantee of the general case – M.M Jun 06 '19 at 01:42
7

There is a misunderstanding here about operator precedence:

(double) -2 * 0

is parsed as

((double)(-(2))) * 0

which is essentially the same as (-2.0) * 0.0.

The C Standard informative Annex J lists as Unspecifier behavior Whether certain operators can generate negative zeros and whether a negative zero becomes a normal zero when stored in an object (6.2.6.2).

Conversely, (double)(-2 * 0) should generate a positive zero 0.0 on most current platforms as the multiplication is performed using integer arithmetic. The C Standard does have support for architectures that distinguish positive and negative zero integers, but these are vanishingly rare nowadays.

If you want to force zeros to be positive, this simple fix should work:

if (d == 0) {
    d = 0;
}

You could make the intent clearer with this:

if (d == -0.0) {
    d = +0.0;
}

But the test will succeed also if d is a positive zero.

Chux has a simpler solution for IEC 60559 complying environments:

d = d + 0.0;  // turn -0.0 to +0.0
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Not recommended to use == with floats or doubles. – Jiminion Jun 22 '17 at 20:21
  • 5
    In general, that's true, but in this particular case, you want to check if `d` is exactly `0.0` or `-0.0` which are indistinguishable with the `==` operator. Comparing to `-DBL_EPSILON` is incorrect as you would turn many small negative non zero values into `0.0`. – chqrlie Jun 22 '17 at 20:26
  • Just realized this is an old post! Likely would not have noticed it had not the deleted answer been given. (I'll take this comment down later) – chux - Reinstate Monica Jun 22 '17 at 21:44
  • 6.2.6.2 only refers to integer negative zeros (which cannot occur in 2's complement) – M.M Jun 06 '19 at 01:38
4

How can I fix that in my application?

Code really is not broken, so nothing needs to be "fixed". @kennytm

How can I fix the result so I get a zero when the result should be zero?

To easily get rid of the - when the result is -0.0, add 0.0. Code following standard (IEC 60559 floating-point) rules will produce drop the - sign.

double nzero = -0.0;
printf("%f\n", nzero);
printf("%f\n", nzero + 0.0);

printf("%f\n", fabs(nzero));  // This has a side effect of changing all negative values

// pedantic code using <math.h>
if (signbit(nzero)) nzero = 0.0; // This has a side effect of changing all negative values
printf("%f\n", nzero);

Usual output.

-0.000000
0.000000
0.000000
0.000000

Yet for general double x that may have any value, hard to beat the following. @Richard J. Ross III @chqrlie The x + 0.0 approach has an advantage in that likely does not introduce a branch, yet the following is clear.

if (x == 0.0) x = 0.0;

Note: fmax(-0.0, 0.0) may produce -0.0.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • What if nzero is equal to -2.0? – Jiminion Jun 22 '17 at 20:20
  • @Jiminion: `nzero` is initialized to `-0.0`. It can be either positive or negative zero, how do you want `nzero` to become `-2.0`? – chqrlie Jun 22 '17 at 20:30
  • 1
    @chux: can you provide a reference from the C Standard to support your claim *To easily get rid of the `-` when the result is `-0.0`, add `0.0`*? – chqrlie Jun 22 '17 at 20:31
  • @chqrlie, because in an actual implementation, you might not know that the value is either 0.0 or -0.0. Only that it might be -0.0; – Jiminion Jun 22 '17 at 20:42
  • 2
    @chqrlie An implementation is not required "... get rid of the - when the result is -0.0, add 0.0?", yet if "An implementation that defines `__STDC_IEC_559__` shall conform to the specifications in this annex" C11 §F.1 and "The +, −, *, and / operators provide the IEC 60559 add, subtract, multiply, and divide operations." §F.3 1 then it does based on that standard. – chux - Reinstate Monica Jun 22 '17 at 20:57
  • 1
    @chqrlie When `nzero` is not NaN, `nzero = nzero + 0.0` will not change the _value_ of `nzero`, yet will cause `-0.0` to become `+0.0` - **if** the implementation follows STDC_IEC_559. Implementations commonly follow _most_ of STDC_IEC_559, even without the `__STDC_IEC_559__`. Of course this is a corner case that it might not. – chux - Reinstate Monica Jun 22 '17 at 21:03
  • 1
    @chux: Good to know. Thank you. Learn something new everyday! – chqrlie Jun 22 '17 at 21:16
  • @Jiminion: if `nzero` is not zero, `nzero == 0` will be false. – chqrlie Jun 22 '17 at 21:17
  • @chqrlie I know it will be false, but your code does not show that. – Jiminion Jun 22 '17 at 21:19
  • @Jiminion: what do you mean? `if (d == 0) { d = 0; }` is pretty clear. Chux proposes a better solution without any tests: `d = d + 0.0;`. If the compiler complies with the IEC 60559 specification, it will convert a negative zero to a positive one and leave all other non-Nan values unchanged. – chqrlie Jun 22 '17 at 21:23
  • @Jiminion Concerning [your code does not show that](https://stackoverflow.com/questions/9657993/negative-zero-in-c/44707475?noredirect=1#comment76401962_44707475): are you referring to this answer? If you refer to another answer, a link to or comment under that answer would make more sense. – chux - Reinstate Monica Jun 22 '17 at 21:24
  • `fabs` is clearly the way to go, it boggles my mind no one mentioned it for five years :-) – Ciro Santilli OurBigBook.com Nov 08 '18 at 08:31
  • 1
    @CiroSantilli新疆改造中心六四事件法轮功 The implied functionality is how to change -0.0 to 0.0 _and all other `double` remain as is_ as in `y = x+0.0`. `fabs()` does not provide this. It domain `x` was only 0.0 and -0.0, `y = 0.0` would be even simpler than `y = fabs(x)`. – chux - Reinstate Monica Nov 08 '18 at 14:34
4

http://en.wikipedia.org/wiki/Signed_zero

The number 0 is usually encoded as +0, but can be represented by either +0 or −0

It shouldn't impact on calculations or UI output.

Aram Kocharyan
  • 20,165
  • 11
  • 81
  • 96
  • 2
    `-0.0` impacts numeric calculations in select cases: [What operations and functions on +0.0 and -0.0 give different arithmetic results?](https://stackoverflow.com/q/25332133/2410359) – chux - Reinstate Monica Jun 22 '17 at 19:12
  • If you print it out, it can look odd and out of place for a human reader. – Jiminion Jun 22 '17 at 20:50
  • @Jiminion In some cases `-0.0` is useful - even if it looks out of place such as values rounded to the near .0 yet want to show which side of 0.0 as in [below freezing](https://en.wikipedia.org/wiki/Signed_zero#Scientific_uses) and financial rounded values to say the nearest million that still want to convey was it a loss, albeit relativity small, as in year-end-profit -0.0 million. – chux - Reinstate Monica Jun 22 '17 at 21:33
1

GCC was seemingly optimizing out the simple fix of negzero += 0.0 as noted above until I realized that -fno-signed-zeros was in place. Duh.

But in the process I did find that this will fix a signed zero, even when -fno-signed-zeros is set:

if (negzero > -DBL_MIN && negzero < DBL_MIN && signbit(negzero))
   negzero = 0.0;                                         

or as a macro:

#define NO_NEG_ZERO(a) ( (a) > -DBL_MIN && (a) < DBL_MIN && signbit(a) ? 0.0 : (a) )

negzero = NO_NEG_ZERO(negzero)

Note that the comparitor is < and > (not <= or >=) so a really is zero! (OR it is a subnormal number...but nevermind the guy behind the curtain.)

Maybe this answer is slightly less correct in the sense that a value of between DBL_MIN and -DBL_MIN will be converted to 0.0, in which case this isn't the way if you need to support subnormal numbers.

If you do need subnormal numbers (!) then perhaps your the kind of person who plays with -fno-signed-zeros, too.

The lesson here for me and subnormal-numbers-guy is this: if you play outside of spec then expect out-of-spec results ;)

(Sorry, that was not PC. It could be subnormal-numbers-person...but I digress.)

KJ7LNW
  • 1,437
  • 5
  • 11
0

In my code (on C MPI intel compiler) -0.0 and +0.0 are not the same.

As an example:

d = -0.0
if (d < 0.0)
    do something...

and it is doing this "something".

also adding -0.0 + 0.0 = -0.0...

Kjartan
  • 18,591
  • 15
  • 71
  • 96
davidT
  • 1