Testing a float for equivalence to a double.

S

Sisyphus

Hi,

I have some software that does the following (in an attempt to determine
whether the double x, can be represented just as accurately by a float):

void test_it(double x) {
float y = x;
if(x == y) {
printf("x can be represented precisely by a float\n");
}
else {
printf("x can't be represented precisely by a float\n");
}
}

Is this an acceptable way of determining whether the double can be
represented precisely by a float ? Is there some other (perhaps
preferable) way of doing it ?

One problem is that, with the above function, Microsoft compilers tell
you that x==y, no matter what the value of x. That strikes me as being
quite bizarre.

I'm wondering whether, to make the code portable, I should trick MS
compilers into doing what I think is right (which can be done fairly
simply) - or whether I should be concentrating on applying some other
method.

Thing is that I don't want to stuff around with getting that code to
work properly with MS compilers if the whole concept of that code is of
dubious merit to begin with.

Cheers,
Rob
 
L

Lawrence Kirby

Hi,

I have some software that does the following (in an attempt to determine
whether the double x, can be represented just as accurately by a float):

void test_it(double x) {
float y = x;
if(x == y) {
printf("x can be represented precisely by a float\n");
}
else {
printf("x can't be represented precisely by a float\n");
}
}

Is this an acceptable way of determining whether the double can be
represented precisely by a float ?

It is as far as standard C is concerned as long as fabs(x) <= FLOAT_MAX.
In the general case you should test for that as well.
Is there some other (perhaps preferable) way of doing it ?

One problem is that, with the above function, Microsoft compilers tell
you that x==y, no matter what the value of x. That strikes me as being
quite bizarre.

Compilers on some platforms, especially x86 ones often choose not to
follow the standard rules exactly because doing so entails a big
performance hit. They prefer to keep values in registers where possible so
they are held at higher precision than they should be.
I'm wondering whether, to make the code portable, I should trick MS
compilers into doing what I think is right (which can be done fairly
simply) - or whether I should be concentrating on applying some other
method.

This is a reasonable approach, and it isn't obvious what else you could
do without examining the representation which won't be portable. Because
we're in a situation where compilers are not following the standard there
is no competely portable method to do this. However making y volatile will
result in the correct behaviour on many compilers.
Thing is that I don't want to stuff around with getting that code to
work properly with MS compilers if the whole concept of that code is of
dubious merit to begin with.

The approach is sensible, it is just a case of making the compiler do the
right thing.

Lawrence
 
B

Bart C

Sisyphus said:
Hi,

I have some software that does the following (in an attempt to determine
whether the double x, can be represented just as accurately by a float):

void test_it(double x) {
float y = x;
if(x == y) { ....
One problem is that, with the above function, Microsoft compilers tell you
that x==y, no matter what the value of x. That strikes me as being quite
bizarre.

Perhaps both are compared as float values, so x is demoted to float and so
will always be the same as y? Or perhaps the parameter x is derived from a
float value elsewhere.

Bart
 
T

Tim Prince

Lawrence Kirby said:
It is as far as standard C is concerned as long as fabs(x) <= FLOAT_MAX.
In the general case you should test for that as well.
Which cases would be affected by such an additional test? Does testing
representations of Infinity invoke UB? If so, does this fix it?
Compilers on some platforms, especially x86 ones often choose not to
follow the standard rules exactly because doing so entails a big
performance hit. They prefer to keep values in registers where possible so
they are held at higher precision than they should be.
Do you consider setting SSE2 code generation as a "trick?"
 
S

Sisyphus

Lawrence said:
This is a reasonable approach, and it isn't obvious what else you could
do without examining the representation which won't be portable. Because
we're in a situation where compilers are not following the standard there
is no competely portable method to do this. However making y volatile will
result in the correct behaviour on many compilers.

Thanks for the reply, Lawrence.

Didn't know about "volatile" - so I've just tried it out, but it doesn't
seem to be the answer in this case.

My solution (so far) is to write a separate function for MS compilers:

#ifdef _MSC_VER // identifies Microsoft compilers
void test_it(double x) {
float foo = x;
float y = foo;
if(x == y) {
printf("x can be represented precisely by a float\n");
}
else {
printf("x can't be represented precisely by a float\n");
}
}
#else // use the original

But that worked only if optimizations were not turned on - in which case
the only solution I could find was to turn 'em off:

#ifdef _MSC_VER
#pragma optimize("", off)
void test_it(double x) {
float foo = x;
float y = foo;
if(x == y) {
printf("x can be represented precisely by a float\n");
}
else {
printf("x can't be represented precisely by a float\n");
}
}
#pragma optimize("", on)
#else

But even that wouldn't work if -O1 optimization (minimize space) were
used - in which case an additional 'foo +=0;' was needed. So, a solution
which I *think* covers all cases with MS compilers is to have:

#ifdef _MSC_VER
#pragma optimize("", off)
void test_it(double x) {
float foo = x;
float y = foo;
foo += 0;
if(x == y) {
printf("x can be represented precisely by a float\n");
}
else {
printf("x can't be represented precisely by a float\n");
}
}
#pragma optimize("", on)
#else

I don't think I've added much overhead to the original (though I haven't
tested) but is there something I could try that is not quite so comical ?

Getting back to "volatile", I found that simply making y volatile did
not achieve the desired result - so I did:

volatile float foo = x;
volatile float y = foo;
if(x == y) { ...

But that still didn't do the right thing, so I added 'foo += 0;' as
before, but that produced something that Microsoft calls an "INTERNAL
COMPILER ERROR".

At that point I gave up on that approach :)

Cheers,
Rob
 
S

Sisyphus

Michael said:
I usually use volatile indirectly:

void test_it(double x) {
float y = x;
if(*((volatile double *)&x) == *((volatile float *)&y)) {
printf("x can be represented precisely by a float\n");
}
else {
printf("x can't be represented precisely by a float\n");
}
}

Thanks for the tip on usage, Michael - though it still doesn't coerce
the MS compiler into behaving as desired :)
Maybe this works for you, too. However, do not forget to check
against the maximum float value...

Sorry - I should have mentioned that this function won't get called if
|x| is greater than the maximum float value. (ie such a condition will
be detected and dealt with before the function is called.)

Cheers,
Rob
 
M

Michael Mair

Sisyphus said:
Thanks for the reply, Lawrence.

Didn't know about "volatile" - so I've just tried it out, but it doesn't
seem to be the answer in this case.

My solution (so far) is to write a separate function for MS compilers:

#ifdef _MSC_VER // identifies Microsoft compilers
void test_it(double x) {
float foo = x;
float y = foo;
if(x == y) {
printf("x can be represented precisely by a float\n");
}
else {
printf("x can't be represented precisely by a float\n");
}
}
#else // use the original

But that worked only if optimizations were not turned on - in which case
the only solution I could find was to turn 'em off:

#ifdef _MSC_VER
#pragma optimize("", off)
void test_it(double x) {
float foo = x;
float y = foo;
if(x == y) {
printf("x can be represented precisely by a float\n");
}
else {
printf("x can't be represented precisely by a float\n");
}
}
#pragma optimize("", on)
#else

But even that wouldn't work if -O1 optimization (minimize space) were
used - in which case an additional 'foo +=0;' was needed. So, a solution
which I *think* covers all cases with MS compilers is to have:

#ifdef _MSC_VER
#pragma optimize("", off)
void test_it(double x) {
float foo = x;
float y = foo;
foo += 0;
if(x == y) {
printf("x can be represented precisely by a float\n");
}
else {
printf("x can't be represented precisely by a float\n");
}
}
#pragma optimize("", on)
#else

I don't think I've added much overhead to the original (though I haven't
tested) but is there something I could try that is not quite so comical ?

Getting back to "volatile", I found that simply making y volatile did
not achieve the desired result - so I did:

volatile float foo = x;
volatile float y = foo;
if(x == y) { ...

But that still didn't do the right thing, so I added 'foo += 0;' as
before, but that produced something that Microsoft calls an "INTERNAL
COMPILER ERROR".

At that point I gave up on that approach :)

I usually use volatile indirectly:

void test_it(double x) {
float y = x;
if(*((volatile double *)&x) == *((volatile float *)&y)) {
printf("x can be represented precisely by a float\n");
}
else {
printf("x can't be represented precisely by a float\n");
}
}

Maybe this works for you, too. However, do not forget to check
against the maximum float value...


Cheers
Michael
 
L

Lawrence Kirby

On Wed, 16 Feb 2005 14:11:14 +0000, Tim Prince wrote:

....
Which cases would be affected by such an additional test? Does testing
representations of Infinity invoke UB? If so, does this fix it?

If you convert a value to float which is not within the range of
representable values of float you get undefined behaviour. Infinities are
not involved here.

Lawrence
 
M

Mark Piffer

Sisyphus said:
Thanks for the tip on usage, Michael - though it still doesn't coerce
the MS compiler into behaving as desired :)

Cheers,
Rob

How about:

void test_it(double x) {
union{ float foo; float bar; } volatile schizo;
schizo.foo = x;
if(x == schizo.bar) {
printf("x can be represented precisely by a float\n");
}
else {
printf("x can't be represented precisely by a float\n");
}
}

I haven't tested it though.

Mark
 
S

Sisyphus

Mark said:
How about:

void test_it(double x) {
union{ float foo; float bar; } volatile schizo;
schizo.foo = x;
if(x == schizo.bar) {
printf("x can be represented precisely by a float\n");
}
else {
printf("x can't be represented precisely by a float\n");
}
}

Not quite (nice try :)

It works with optimization -O1, but fails with both -O2 and with no
optimizations.

Cheers,
Rob
 

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,997
Messages
2,570,240
Members
46,828
Latest member
LauraCastr

Latest Threads

Top