Reliably determine sign value of double in C90/C89

A

artifact.one

Hello.

I want to be able to portably determine whether or not a given double
value is negative.

Under C99, I do:

if (signbit(d)) negative = 1;

Or, if the implementation doesn't provide signbit(), I do:

union real {
unsigned long long n;
double d;
};

if ((real.n >> 63) & 1) negative = 1;

However, what if I am to support an implementation that only provides
C89 (no long long, no signbit())?

Anybody got a reliable, portable method of handling this?

thanks,
mc
 
F

Flash Gordon

Hello.

I want to be able to portably determine whether or not a given double
value is negative.

Under C99, I do:

if (signbit(d)) negative = 1;

However, what if I am to support an implementation that only provides
C89 (no long long, no signbit())?

Anybody got a reliable, portable method of handling this?

What is wrong with
if (d < 0)
?

It won't detect negative 0, but is that important to you?
 
D

Dave Vandervies

Hello.

I want to be able to portably determine whether or not a given double
value is negative.
[...]

Anybody got a reliable, portable method of handling this?

Why isn't "double_val<0", possibly combined with tests for magic values
like NaN that will or won't pass this test when they shoudn't or should,
reliable or portable enough?

(I am not a numerical analyst; there may be a valid reason why not.
But if you're asking C programmers who aren't numerical analysts how to
do this, you should be able to tell us why the obvious solution doesn't
meet your standards.)


dave
 
A

Army1987

Hello.

I want to be able to portably determine whether or not a given double
value is negative.

Under C99, I do:

if (signbit(d)) negative = 1;

Or, if the implementation doesn't provide signbit(), I do:

union real {
unsigned long long n;
double d;
};

if ((real.n >> 63) & 1) negative = 1;

However, what if I am to support an implementation that only provides
C89 (no long long, no signbit())?

Anybody got a reliable, portable method of handling this?

if (d < 0) negative = 1;
 
E

Eric Sosman

Army1987 wrote On 04/27/07 17:18,:
Then use if (d != fabs(d))

Won't work; -0 is equal to +0, and there are problems
with NaNs and infinities, too, if they exist. That's
probably why signbit() exists, and I think it's the best
solution to artifact's problem.

Suggestion: Write the code to use signbit(), and use
the system-provided signbit() if one exists. For platforms
where it doesn't, provide your own platform-specific code
to implement it. 100% portability can't be guaranteed; in
particular, the kludge with a union will not always work.
(The sign bit may not be in the position where you expect
it, `long' and `double' may not be the same size, ...)
 
A

artifact.one

Why isn't "double_val<0", possibly combined with tests for magic values
like NaN that will or won't pass this test when they shoudn't or should,
reliable or portable enough?

(I am not a numerical analyst; there may be a valid reason why not.
But if you're asking C programmers who aren't numerical analysts how to
do this, you should be able to tell us why the obvious solution doesn't
meet your standards.)

The simple answer is that I need to be able to detect negative zero
(after
doing all appropriate checks for NaN, inf, -inf etc.

MC
 
M

Martin Ambuhl

Hello.

I want to be able to portably determine whether or not a given double
value is negative.

inline int negativep(double x) { return x < 0; }
Under C99, I do:

if (signbit(d)) negative = 1;

see above.
Or, if the implementation doesn't provide signbit(), I do:

union real {
unsigned long long n;
double d;
};

if ((real.n >> 63) & 1) negative = 1;

see above.
However, what if I am to support an implementation that only provides
C89 (no long long, no signbit())?

see above.
Anybody got a reliable, portable method of handling this?

see above.
 
A

artifact.one

Army1987 wrote On 04/27/07 17:18,:





Won't work; -0 is equal to +0, and there are problems
with NaNs and infinities, too, if they exist. That's
probably why signbit() exists, and I think it's the best
solution to artifact's problem.

Suggestion: Write the code to use signbit(), and use
the system-provided signbit() if one exists. For platforms
where it doesn't, provide your own platform-specific code
to implement it. 100% portability can't be guaranteed; in
particular, the kludge with a union will not always work.
(The sign bit may not be in the position where you expect
it, `long' and `double' may not be the same size, ...)

Hello.

I have taken care of most of these possibilities, as far as I can
tell.

The code first checks for infinity and negative infinity by using
whichever of isinf(), isfinite() or finite() is available.

Then it checks for infinity with isnan() which seems to be
portable.

Then it checks for negative zero with the following:

#if defined(HAVE_LONGLONG)
union real {
double d;
unsigned long long n;
};
#else
union real {
double d;
unsigned long n;
};
#endif

#if !defined(HAVE_INLINE)
#define inline
#endif

static inline unsigned int is_negative(double d)
{
union real real;
real.d = d;

#if defined(HAVE_MATH_SIGNBIT)
return signbit(real.d);
#endif

if (sizeof(real.n) == sizeof(double) == 64)
return (real.n >> 63) & 1;
else
return real.d != fabs(real.d);
}

I'm sure this can be improved, I'm just not sure how yet...

MC
 
F

Flash Gordon

Yes, unfortunately it is.

Ah well, for C90 you are down to system specifics and #if/#ifdef then.
Remember there is the IEEE stuff which will help with a number of
implementations.
 
D

Dave Vandervies

if (sizeof(real.n) == sizeof(double) == 64)

This won't do what you seem to expect it to.

You probably want
--------
if( CHAR_BIT==8
&& sizeof real.n == 8 /* => real.n is a 64-bit integer type*/
&& sizeof real.d == 8) /* => real.d is a 64-bit floating point type*/
--------
(Don't forget to #include <limits.h> for CHAR_BIT) or possibly just
--------
if(sizeof real.n == sizeof real.d)
--------

#if defined(HAVE_MATH_SIGNBIT)
return signbit(real.d);
#endif

if (sizeof(real.n) == sizeof(double) == 64)
return (real.n >> 63) & 1;
else
return real.d != fabs(real.d);

I would write this as
--------
#if defined(HAVE_MATH_SIGNBIT)
return signbit(real.d);
#elif defined(HAVE_KNOWN_DOUBLE_FORMAT)
#ifdef HAVE_RIGHT_SIZE_INTEGER
return (real.n >> 63) & 1;
#else
/*Code to extract sign bit from double's bit pattern*/
#endif
#else
return real.d != fabs(real.d);
#endif
--------
since you should know at compile time (through feature macros if nothing
else) what properties the implementation has, so there's no point in
compiling code that will never run.


dave
 
J

jacob navia

Hello.

I want to be able to portably determine whether or not a given double
value is negative.

Under C99, I do:

if (signbit(d)) negative = 1;

Or, if the implementation doesn't provide signbit(), I do:

union real {
unsigned long long n;
double d;
};

if ((real.n >> 63) & 1) negative = 1;

However, what if I am to support an implementation that only provides
C89 (no long long, no signbit())?

Anybody got a reliable, portable method of handling this?

thanks,
mc

signbit can't be written really efficiently and correctly in C.
Other languages fare much better:
.text
.globl _signbit
_signbit:
fldl 4(%esp) ; load double into FPU
fxam ; Examine it
fnstsw %ax ; Store status word
test $512,%ax; Test sign bit
setne %al ; set al to 1 if negative
andl $1,%eax ; clear other jits of eax
fstp %st(0) ; pop number from FPU
ret ; done
21 bytes code size, and an unmatched speed, just 8
machine instructions.

This is quite portable, since it will run under
Linux (x86) Solaris(x86) MacIntosh(x86) Windows
(all versions) Beos (x86) and MANY embedded
systems.
 
A

artifact.one

You probably want

Yes, you're absolutely right. That's the second time I've made that
mistake.
I would write this as
--------
#if defined(HAVE_MATH_SIGNBIT)
return signbit(real.d);
#elif defined(HAVE_KNOWN_DOUBLE_FORMAT)
#ifdef HAVE_RIGHT_SIZE_INTEGER
return (real.n >> 63) & 1;
#else
/*Code to extract sign bit from double's bit pattern*/
#endif
#else
return real.d != fabs(real.d);
#endif
--------
since you should know at compile time (through feature macros if nothing
else) what properties the implementation has, so there's no point in
compiling code that will never run.

dave

Yes, I will make these adjustments. For now I will only support IEEE
754
doubles.

MC
 
K

Keith Thompson

The simple answer is that I need to be able to detect negative zero
(after doing all appropriate checks for NaN, inf, -inf etc.

I don't believe the C90 standard has any concept of negative zero,
NaN, or Inf. Assuming I'm correct on that point, there is no portable
way to distinguish between -0.0 and +0.0; an implementation is allowed
to treat them completely identically, apart from their different
representations. You're probably stuck with some non-portable
solution using a system-specific library.

Something that *might* work: if a value compares equal to 0.0 but has
a different representation than 0.0 (use memcmp), then it's *probably*
-0.0. But don't take my word for that.
 
K

Kenneth Brody

Yes, unfortunately it is.

What does the standard say about this:

const double PositiveZero = 0.0;
const double NegativeZero = -0.0;

Are they guaranteed to be different?

(Yes, I know "positive zero" is an oxymoron, but then again, so is
"negative zero".)

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
K

Keith Thompson

jacob navia said:
signbit can't be written really efficiently and correctly in C.
Other languages fare much better:
.text
.globl _signbit
_signbit:
fldl 4(%esp) ; load double into FPU
fxam ; Examine it
fnstsw %ax ; Store status word
test $512,%ax; Test sign bit
setne %al ; set al to 1 if negative
andl $1,%eax ; clear other jits of eax
fstp %st(0) ; pop number from FPU
ret ; done
21 bytes code size, and an unmatched speed, just 8
machine instructions.

This is quite portable, since it will run under
Linux (x86) Solaris(x86) MacIntosh(x86) Windows
(all versions) Beos (x86) and MANY embedded
systems.

Your definitiono "quite portable" is, to say the least, unusual.

It is, to say the most, nonsense.
 
K

Keith Thompson

--
Dave Vandervies (e-mail address removed)

Surprise your compiler. Write better code than it asks you to.
--Keith Thompson in comp.lang.c

Love the sig quote!
 
A

artifact.one

What does the standard say about this:

const double PositiveZero = 0.0;
const double NegativeZero = -0.0;

Are they guaranteed to be different?

(Yes, I know "positive zero" is an oxymoron, but then again, so is
"negative zero".)

It's hard to say really. I can't see anything in N1124 that says they
have to be different, but then it also speaks of IEEE 754, which says
that they do. I suppose this could mean that it *implies* it...

MC
 

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,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top