Guz
Guz

Reputation: 213

How to convert negative zero to positive zero in C?

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?

Upvotes: 21

Views: 25841

Answers (6)

KJ7LNW
KJ7LNW

Reputation: 1901

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.)

Upvotes: 1

davidT
davidT

Reputation: 1

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...

Upvotes: 0

chux
chux

Reputation: 153338

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.

Upvotes: 4

chqrlie
chqrlie

Reputation: 144695

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

Upvotes: 8

Aram Kocharyan
Aram Kocharyan

Reputation: 20421

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.

Upvotes: 4

Richard J. Ross III
Richard J. Ross III

Reputation: 55533

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;

Upvotes: 19

Related Questions