Why the zero case in fabs()?

M

Martin

In Plauger's THE STANDARD C LIBRARY (1992) there is the source code for
fabs.c (p140).

/* fabs function */
#include "xmath.h"
double (fabs)(double x)
{
switch (_Dtest(&x))
{ /* test for special codes */
case NAN:
errno = EDOM;
return (x);
case INF:
errno = ERANGE;
return (_Inf._D);
case 0:
return (0.0);
default: /* finite */
return (x < 0.0 ? -x : x);
}
}

This is not necessarily a question about fabs() in particular, but rather a
question about the *technique* Plauger has employed, and if there is a
subtlety involved.

My question is: why is there a case for 0, which simply returns 0.0? Without
it, the default case will trap it anyway, and return 0, which will be
converted to double - the return type of the function.

Also, I'm puzzled by the superfluous use of parentheses around the return
expressions.

Martin
http://martinobrien.co.uk/
 
D

Dan Pop

In said:
In Plauger's THE STANDARD C LIBRARY (1992) there is the source code for
fabs.c (p140).

/* fabs function */
#include "xmath.h"
double (fabs)(double x)
{
switch (_Dtest(&x))
{ /* test for special codes */
case NAN:
errno = EDOM;
return (x);
case INF:
errno = ERANGE;
return (_Inf._D);
case 0:
return (0.0);
default: /* finite */
return (x < 0.0 ? -x : x);
}
}

This is not necessarily a question about fabs() in particular, but rather a
question about the *technique* Plauger has employed, and if there is a
subtlety involved.

My question is: why is there a case for 0, which simply returns 0.0? Without
it, the default case will trap it anyway, and return 0, which will be
converted to double - the return type of the function.

No conversion to double is needed or would occur in that case: the
function would return x which is already of type double.

However, I don't know why Plauger handled 0.0 as a special case.
Also, I'm puzzled by the superfluous use of parentheses around the return
expressions.

They were mandatory, back when Plauger started to use C. Old habits die
hard...

Dan
 
L

lawrence.jones

Martin said:
My question is: why is there a case for 0, which simply returns 0.0? Without
it, the default case will trap it anyway, and return 0, which will be
converted to double - the return type of the function.

The subtlety you're missing is signed zeros. On implementations that
have such things, -0.0 is not < 0.0, so the default case would return it
as-is (i.e., -0.0) rather than returning the correct +0.0.
Also, I'm puzzled by the superfluous use of parentheses around the return
expressions.

Style. Or lack thereof, if we're making value judgments. :)

-Larry Jones

I don't think that question was very hypothetical at all. -- Calvin
 
M

Martin Dickopp

Martin said:
In Plauger's THE STANDARD C LIBRARY (1992) there is the source code for
fabs.c (p140).

/* fabs function */
#include "xmath.h"
double (fabs)(double x)
{
switch (_Dtest(&x))
{ /* test for special codes */
case NAN:
errno = EDOM;
return (x);
case INF:
errno = ERANGE;
return (_Inf._D);
case 0:
return (0.0);
default: /* finite */
return (x < 0.0 ? -x : x);
}
}

This is not necessarily a question about fabs() in particular, but
rather a question about the *technique* Plauger has employed, and if
there is a subtlety involved.

My question is: why is there a case for 0, which simply returns 0.0?
Without it, the default case will trap it anyway, and return 0, which
will be converted to double - the return type of the function.

I don't know what `_Dtest' does, but presumably it returns 0 if `x' is
0.0. If it is already known that `x' is 0.0, there is no reason to
compare it with 0.0 again, which would only (slightly) degrade the
performance.
Also, I'm puzzled by the superfluous use of parentheses around the
return expressions.

So am I.

Martin
 
G

Gordon Burditt

This is not necessarily a question about fabs() in particular, but rather a
question about the *technique* Plauger has employed, and if there is a
subtlety involved.

My question is: why is there a case for 0, which simply returns 0.0? Without
it, the default case will trap it anyway, and return 0, which will be
converted to double - the return type of the function.

Is negative zero an issue here? Are there some implementations
(possibly buggy by current floating point standards) where negative
zero tests as less than 0? Perhaps it was desired that fabs() never
return anything that tests less than zero, not even negative zero,
even for implementations of what later became IEEE floating point
that didn't quite handle all the corner cases involving negative
zero according to the current floating point standard.

Gordon L. Burditt
 
C

Christian Bau

"Martin said:
In Plauger's THE STANDARD C LIBRARY (1992) there is the source code for
fabs.c (p140).

/* fabs function */
#include "xmath.h"
double (fabs)(double x)
{
switch (_Dtest(&x))
{ /* test for special codes */
case NAN:
errno = EDOM;
return (x);
case INF:
errno = ERANGE;
return (_Inf._D);
case 0:
return (0.0);
default: /* finite */
return (x < 0.0 ? -x : x);
}
}

This is not necessarily a question about fabs() in particular, but rather a
question about the *technique* Plauger has employed, and if there is a
subtlety involved.

My question is: why is there a case for 0, which simply returns 0.0? Without
it, the default case will trap it anyway, and return 0, which will be
converted to double - the return type of the function.

One possibility is that he wanted floating point numbers in IEEE 754
format to be handled correctly: In that format, there are two different
representations for +0.0 and -0.0; both produce values that compare
equal to x, but you would want fabs (-0.0) to be +0.0.
 
P

P.J. Plauger

One possibility is that he wanted floating point numbers in IEEE 754
format to be handled correctly: In that format, there are two different
representations for +0.0 and -0.0; both produce values that compare
equal to x, but you would want fabs (-0.0) to be +0.0.
Correct.

It's a style I settled on many years ago. (FWIW and IIRC, there was a
brief period when the parentheses were required by an early Ritchie
compiler.) We made it part of the shop standard at Whitesmiths and
I've stuck with it over the years.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
 
M

Martin

"P.J. Plauger" commented:

"P.J. Plauger" replied
It's a style I settled on many years ago. (FWIW and IIRC, there was a
brief period when the parentheses were required by an early Ritchie
compiler.) We made it part of the shop standard at Whitesmiths and
I've stuck with it over the years.

My thanks to P.J. Plauger, Christian, Gordon, Martin Dicksopp, Lawrence, and
Dan for your reponses.

THE STANDARD C LIBRARY is packed with much information I know, and is a
large enough book, but perhaps that subtlety with the IEEE 754 should have
been pointed out (brief comment in the code?).
 
C

CBFalconer

Martin said:
In Plauger's THE STANDARD C LIBRARY (1992) there is the source
code for fabs.c (p140).

/* fabs function */
#include "xmath.h"
double (fabs)(double x)
{
switch (_Dtest(&x))
{ /* test for special codes */
case NAN:
errno = EDOM;
return (x);
case INF:
errno = ERANGE;
return (_Inf._D);
case 0:
return (0.0);
default: /* finite */
return (x < 0.0 ? -x : x);
}
}

This is not necessarily a question about fabs() in particular,
but rather a question about the *technique* Plauger has employed,
and if there is a subtlety involved.

My question is: why is there a case for 0, which simply returns
0.0? Without it, the default case will trap it anyway, and return
0, which will be converted to double - the return type of the
function.

The switch is not testing x, but whatever the return value of
_Dtest(&x) happens to be. That obviously involves some magic
incantations, and is outside the users name space. It might well
have something to do with the existance of -ve 0 in that
particular arithmetic system.
Also, I'm puzzled by the superfluous use of parentheses around
the return expressions.

Superfluous is in the eye of the beholder. Plauger, and others,
feel that there is no further need to ration parentheses and that
they can be neatly used to delimit expressions.
 
D

Dan Pop

In said:
The subtlety you're missing is signed zeros. On implementations that
have such things, -0.0 is not < 0.0, so the default case would return it
as-is (i.e., -0.0) rather than returning the correct +0.0.

If -0.0 is not < 0.0, why is it an error to return it as such?

Dan
 
L

lawrence.jones

Dan Pop said:
If -0.0 is not < 0.0, why is it an error to return it as such?

I won't defend the vagaries of signed zeros, but although -0.0 and +0.0
compare equal (obviously), -0.0 is nonetheless negative whereas the
result of fabs() should (obviously) be non-negative. Despite comparing
equal, -0.0 and +0.0 can still be distingished (e.g., by the signbit()
or copysign() functions).

-Larry Jones

I don't need to improve! Everyone ELSE does! -- Calvin
 
C

Christian Bau

[email protected] (Dan Pop) said:
If -0.0 is not < 0.0, why is it an error to return it as such?

If the implementor guarantees that the compiler implements not only the
C Standard, but also for example the IEEE 754 Standard then returning
-0.0 as the result of fabs (-0.0) is an error. It would violate the IEEE
754 Standard.
 

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
474,140
Messages
2,570,810
Members
47,357
Latest member
sitele8746

Latest Threads

Top