How to test for -0.0?

N

Nate Bargmann

I am working on a function that takes degrees, minutes, seconds
coordinates and converts them to decimal representation. Traditionally,
in DMS notation the '-' sign, to indicate west longitude or north
latitude, precedes the degree value, e.i. -96° 59' 59". The float type
is capable of carrying a signed ZERO for the purpose of representing
between 0 and 1 degree west or south.

My problem comes from trying to select the correct algorithm for computing
a positive or negative value. To illustrate, here is the function:

double dms2dec(float degrees, int minutes, double seconds) {
if (degrees < 0 || degrees == -0.0) {
return (double)degrees - (double)minutes/60. - seconds/3600.;
} else {
return (double)degrees + (double)minutes/60. + seconds/3600.;
}
}

This function has an error in that it treats all instances of degrees,
whether -0.0 or 0.0, as negative and executes the first statement. If
degrees is less than or equal to -1 or greater than or equal to 1, the
function returns the expected result. If degrees is greater than -1 but
less than 0.0 the calculation is correct. When degrees is greater than
0.0 but less than 1, the error occurs.

My question, how can I properly test the degrees argument and make a
correct decision based on whether degrees is -0.0 or 0.0? Perhaps the
best solution involves the use of copysign() to store the sign, performing
the calculation as a positive value then signing the result, if necessary.

What do the gurus think?

- Nate >>
 
C

Coos Haak

double dms2dec(float degrees, int minutes, double seconds) {

Why not define
dms2dec(int degrees, int minutes, double seconds);

And have functions that return degrees reduce them to -180 to 180
or 0 to 360.
 
N

Nate Bargmann

Just so everyone knows, I did get it working to my satisfaction by using
copysign() and then signing the result.

Why not define
dms2dec(int degrees, int minutes, double seconds);

And have functions that return degrees reduce them to -180 to 180 or 0 to
360.

Originally, degrees was an int, but GNU C doesn't sign the zero. This
function is part of a library project, so it's important to follow
established convention. To use int it seems as though a separate flag
value had to be used or the minutes or seconds values had to be signed.
Neither matches real world usage so the property of a float to carry a
signed zero works well.

Incidentally, my revised and working function:

double dms2dec(float degrees, int minutes, double seconds) {
double s, st;

s = copysign(1.0, (double)degrees);
st = fabs((double)degrees);

return copysign((st + (double)minutes / 60. + seconds / 3600.), s);
}

- Nate >>
 
G

Glen Herrmannsfeldt

Nate Bargmann said:
I am working on a function that takes degrees, minutes, seconds
coordinates and converts them to decimal representation. Traditionally,
in DMS notation the '-' sign, to indicate west longitude or north
latitude, precedes the degree value, e.i. -96° 59' 59". The float type
is capable of carrying a signed ZERO for the purpose of representing
between 0 and 1 degree west or south.
(snip)

This function has an error in that it treats all instances of degrees,
whether -0.0 or 0.0, as negative and executes the first statement. If
degrees is less than or equal to -1 or greater than or equal to 1, the
function returns the expected result. If degrees is greater than -1 but
less than 0.0 the calculation is correct. When degrees is greater than
0.0 but less than 1, the error occurs.

For degrees/minutes/seconds form it would not be normal for degrees to be a
floating point type.

While floating point, on many machines, has the ability to store -0.0, it is
unusual for library conversion routines to generate it.

Your routine should accept a character string and do all conversions,
including the recognition of - or +, itself. You could also accept E or W
for east/west longitude, if that were useful.

-- glen
 
L

lawrence.jones

Nate Bargmann said:
My question, how can I properly test the degrees argument and make a
correct decision based on whether degrees is -0.0 or 0.0? Perhaps the
best solution involves the use of copysign() to store the sign, performing
the calculation as a positive value then signing the result, if necessary.

C99 has a signbit() function to do the test directly.

-Larry Jones

My dreams are getting way too literal. -- Calvin
 
R

Richard A. Litherland

Nate Bargmann said:
Just so everyone knows, I did get it working to my satisfaction by using
copysign() and then signing the result.

Your code below also works on my hardware/implementation combination
(gcc and glibc on an i686). However, C99 does not guarantee its
portability. (C89 knows nothing of copysign().) From 7.12.11.1:

[#2] The copysign functions produce a value with the
magnitude of x and the sign of y. They produce a NaN (with
the sign of y) if x is a NaN. On implementations that
represent a signed zero but do not treat negative zero
consistently in arithmetic operations, the copysign
functions regard the sign of zero as positive.

As far as I can tell the standard does not require an implementation
to ever produce a negative zero in any floating type, nor (if it does)
to distinguish it from a positive zero in any meaningful or consistent
way. Things are different if your implementation defines
__STDC_IEC_559__, but gcc, for instance, does not, and claims
compliance with IEC 559 "if and only if the hardware is perfectly
compliant" (http://gcc.gnu.org/gcc-3.3/c99status.html).
Originally, degrees was an int, but GNU C doesn't sign the zero. This
function is part of a library project, so it's important to follow
established convention. To use int it seems as though a separate flag
value had to be used or the minutes or seconds values had to be signed.
Neither matches real world usage so the property of a float to carry a
signed zero works well.

I don't follow the reasoning here at all. I can see that it's
important to allow the user to enter values using the established
convention, but that doesn't dictate your program's internal
representation of the value entered. To me, using float to represent
an integral quantity purely so you can get a negative zero is much
less natural than using a flag. That's just my opinion, though.
Incidentally, my revised and working function:

double dms2dec(float degrees, int minutes, double seconds) {
double s, st;

s = copysign(1.0, (double)degrees);
st = fabs((double)degrees);

return copysign((st + (double)minutes / 60. + seconds / 3600.), s);
}

And I'd leave out all the casts to double.

Rick.
 
L

lawrence.jones

Glen Herrmannsfeldt said:
But does the library guarantee to store a -0.0f if one puts -0 into scanf()?

Maybe. The standard doesn't explicitly say much about the conversions
done by scanf but it does refer to other library functions (e.g.,
strtol, strtod) to define the format of the input items, so it's
reasonable to conclude that those library functions also define the
conversions. In that case, strtod is required to honor the sign of zero
on implementations that support signed zeros.

-Larry Jones

I'm writing you a message in code. How do you spell "nincompoop"? -- Calvin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,280
Latest member
BGBBrock56

Latest Threads

Top