Negative zero causing error

I have written a function that returns the quadrant for a given angle. When I give the value of y after decrementing it 10 time in the for loop to the function angle_quad(), the value returned by the function is zero when the value of y is -0.0.

When I set the value of y as -0.0 in method-2 I get the expected answer, why? I searched for this negative zero error, most of the result I got was that negative zero is defined in double or float and it shouldn’t cause any problems as 0 == -0.0 in double or float. Some suggest using

if(y == -0.0) y=0;

to mitigate the problem in the application. What should be done to solve this problem? The function can’t give out one result for negative zero and another for just zero. Is the function definition at fault here with the if else if statements?

I am using the Arduino Due.

Thanks

double x=0.1125;
double y=1.125; //0.1125 * 10

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);

//Method-1
for(int i=1;i<=10;i++) //Decrementing x from y by 10 times
{
  y=y-x; //Decrementing
  Serial.println(y,DEC);
}
Serial.println("\ny: "+String(y,DEC)); //Final result after the decrement 
int8_t rquad=angle_quad(y); //Giving the value of y obtained to the function
Serial.println("Returned value: "+String(rquad)+"\n"); //Result is not correct: 0

//Method-2
y=-0.0; //Setting negative zero
Serial.println("\ny: "+String(y,DEC)); //Printing y to observe the value
rquad=angle_quad(y); //Giving it to the function again
Serial.println("Returned value: "+String(rquad)+"\n"); //Result is correct: 2

//Why is both methods not returning the same value? I thought negative zero and zero equated to true. Is it not defined in the compiler for double and float?
}

void loop() {
  // put your main code here, to run repeatedly:

}

int8_t angle_quad(double angle) //Function that returns bad result
{
  int8_t quad=0; //This causes the function to return 0 when none of the condition below is satisfied
  if (angle >= 270.0 && angle <= 360.0) return quad = 1; 
  else if (angle >= 0.0 && angle <= 90.0) return quad = 2; 
  else if (angle >= 90.0 && angle <= 270.0)
  {
    Serial1.println("ERROR!");
  }
}

I don’t have a due with me

can you try this code

void setup() {
  Serial.begin(115200);
  if ((double) - 0.0 >= (double) 0.0) Serial.println("-0.0 is not greater or equal to 0.0");
  if ((double) - 0.0 <= (double) 0.0) Serial.println("-0.0 is smaller or equal to 0.0");
  Serial.println((double) -0.0, DEC);
}

void loop() {}

on a UNO is prints

-0.0 is not greater or equal to 0.0
-0.0 is smaller or equal to 0.0
0.0000000000

while it’s a bit shocking, I guess you could argue that because double or float do not have infinite precision, if you give a hint to a compiler that this is approaching zero from the negative side, then it is indeed below zero…

smart compiler? oh well…

Surely negative zero won't crop up normally, you have to underflow with underflow's not trapped.

Perhaps this is float library bug.

BTW it is never a good idea (ie a bug) to rely on floating point equality, as rounding errors will
mean the answer is rather arbitrary.

J-M-L, I tried your code and I got the same result as you did.

-0.0 is not greater or equal to 0.0
-0.0 is smaller or equal to 0.0
0.0000000000

I tried this too, but the error only happens when decrementing from some number to zero. I get the error only when using the for loop or when doing the calculation in the main loop.

MarkT:
Surely negative zero won’t crop up normally, you have to underflow with underflow’s not trapped.

Perhaps this is float library bug.

BTW it is never a good idea (ie a bug) to rely on floating point equality, as rounding errors will
mean the answer is rather arbitrary.

Most of the time I use >= , <=, < or > to compare between two floating point or double numbers. When I try == to check two floating point numbers, sometimes the condition is not satisfied and the code doesn’t get executed because of the precision of double and float. That is why I used fabs() function to get rid of the negative zero problem instead of using if(y == -0.0) y=0;.
But what will u do when you need negative values and this kind of error happens?

I know that converting double to int by multiplying the double with some order of 10 and then casting it as int is possible, but wouldn’t it tax the processor of the microcontroller when converting between int and double for calculations or is it better to use int?

The language is trying hard to obey the rules of mathematics. There isn't really any such thing as minus zero, it's a mathematical oxymoron. The syntax allows it because permitting it and allowing it to be equivalent to zero, is harmless. That's because values may be generated in code, not by humans.

If you are in a situation where you "need" a negative zero, you have made some kind of semantic design error.

In mathematics when you study limits you can be at 0+ or 0- depend how you are getting there. I.e. It will never be zero but almost. You can't represent almost with infinite precision but -0.0 is as close as you get to being just under zero while mathematically being 0 in operations such as adding or subtracting.

Thanks for the reply J-M-L. I solved the problem by using the fabs() math function

y=fabs(y)

This gets rid of the negative zero problem but I don't know whether it's a good solution to the problem.

Sure that would kinda work for zero but -10 deg and 10deg are not in the same quadrant.

I set the first quadrant from 270 to 360 and then from 0 to 90 for the second quadrant. In this way I eliminated the need for negative angle for distinguishing between quadrants. I realise that what I said as first quadrant is actually the 4th quadrant and the 2nd quadrant is the first quadrant. I just changed it for convinence as these ranges of angles are used in another function.

What you said is true if negative angles are present. I will keep digging for a solution. The current solution I am using doesn't sit right with me. At least it works.

Do you really need double for your calculations?

Well, I need some amount of precision as some of the other functions are working with quite small angles and I decided to use double where precision is needed and float where it is less important. In Arduino Due double is 8 bytes and float is 4 bytes. So double can hold much more of the fractional part without much rounding error or so I thought.

Yep you are correct - 8 bytes is better than 4 :slight_smile:

See Signed zero

In general though, for floating point calculations comparing “equal” is fraught with danger, because numbers may look equal but differ by a tiny amount. You normally should compare for “less than” an acceptable variation amount. For example, if you consider your height to be 180 cm and another person is 180 cm, you would have a fudge factor (eg. 1 mm).

Thus you could say:

if (fabs (myHeight - hisHeight) < 0.001)
  {
  // height is "equal"
  }

So rather than testing for equality you are checking for ± an acceptable range.