Operating System - OpenVMS
1753943 Members
9426 Online
108811 Solutions
New Discussion юеВ

104.0 > 100000001: C 6.5 bug?

 
SOLVED
Go to solution
Craig A Berry
Honored Contributor

104.0 > 100000001: C 6.5 bug?

My environment:

$ cc/version
Compaq C V6.5-001 on OpenVMS Alpha V7.3-1

Consider the following test program:

$ type relcomp.c
#include
#define BIGNUM 100000001
int
main() {
float x = 104.0;
( x <= BIGNUM ?
printf("%f <= %d\n", x, BIGNUM)
: printf("%f NOT <= %d !!!\n", x, BIGNUM) );
}

Most compile options cause this program to yield the expected result, namely that 104.0 is less than a 9-digit integer. However, with /FLOAT=IEEE/IEEE=DENORM_RESULTS, the opposite happens:

$ cc/float=ieee/ieee=denorm relcomp
$ link relcomp
$ run relcomp
104.000000 NOT <= 100000001 !!!

If I understand the C standard on how this should work, the first thing that should happen in the comparison "104.0 <= 100000001" is that 100000001 should be converted to a double. That's because all operands of the relational operators (when they are not pointers) must be of real type, and the real type required to hold 100000001 is a double. Then the single precision value 104.0 should also be converted to a double since when comparing two types in the same type domain (in this case two real types), everything should be converted to the type with the greater range. At last the comparison should take place between two doubles.

Something about /IEEE=DENORM seems to throw a wrench in the procedure I've described above. I'd suspect 100000001 is being converted to a single precision real that of course can't hold the value and denormalization suppresses any exceptions while picking a number that can be represented as a single, a number that apparently turns out to be less than 104. That's just a hunch and I've spared myself generating a machine listing to see what actually happens since Alpha assembler and I are not well acquainted with each other.

So, is this a compiler bug, or is there some rationale for what happens?
14 REPLIES 14
Willem Grooters
Honored Contributor

Re: 104.0 > 100000001: C 6.5 bug?

I guess the crux is

#define BIGNUM 100000001 int

I altered the program a bit (for what I can tell functional the same)

#include
#define BIGNUM 100000001
int
main() {
float x = 104.0;
float y = BIGNUM * 1.0 ; <------ ADDED
( x <= y ? <------ modified
printf("%f <= %d\n", x, BIGNUM)
: printf("%f NOT <= %d !!!\n", x, BIGNUM) );
}
and then the output IS correct:

BPS2004A> run t
104.000000 <= 100000001
BPS2004A>

I don't know C that well, I would say: compiler bug.
Willem Grooters
OpenVMS Developer & System Manager
Antoniov.
Honored Contributor

Re: 104.0 > 100000001: C 6.5 bug?

Hi Craig,
this is weird or a bug of optimizer; if you use standard form

if ( x <= BIGNUM)
printf("%f <= %d\n", x, BIGNUM);
else
printf("%f NOT <= %d !!!\n", x, BIGNUM;

program fails however?

Also it's better write
#define 100000001.0
so you avoid some cast conversion and try using CC/NOOPT.

I complied your example but my old CC (V5) works fine and I can't help you.

Bye
Antoniov

P.S.
Because using CC V5 your program works, I think it's a bug of V6.
Antonio Maria Vigliotti
Willem Grooters
Honored Contributor

Re: 104.0 > 100000001: C 6.5 bug?

It's not the optimizer, I tried when building for debug. Compiling with /NOOPT only didn't change the behaviour either.
Willem Grooters
OpenVMS Developer & System Manager
Willem Grooters
Honored Contributor

Re: 104.0 > 100000001: C 6.5 bug?

Put it all in one:

#include
#define BIGNUM 100000001
int
main() {
float x = 104.0;
( x <= BIGNUM ?
printf("%f <= %d\n", x, BIGNUM)
: printf("%f NOT <= %d !!!\n", x, BIGNUM) );

float y = BIGNUM * 1.0 ;
( x <= y ? printf("%f <= %d\n", x, BIGNUM) : printf("%f NOT <= %d !!!\n", x, BIGNUM) );

if ( x <= BIGNUM)
printf("%f <= %d\n", x, BIGNUM);
else
printf("%f NOT <= %d !!!\n", x, BIGNUM);
}

compiled /FLOAT=IEEE/IEEE=DENORM_RESULTS, output is:

104.000000 NOT <= 100000001 !!!
104.000000 <= 100000001
104.000000 NOT <= 100000001 !!!

same with /NOOPT.

Adding ".0" to BIGNUM as Antonio suggest gives compliation warnings %CC-W-OUTFLOATINT (obviously) on th eprintf for output, but results are musch more in line with expectations:

104.000000 <= 67108864
104.000000 <= 67108864
104.000000 <= 67108864

Ok, the output IS wrong but it seems the IF statement is Ok.

It _could_ be a compiler bug, but, on the other hand, you _ought_ to know that you're comparing float to int in this example. So it could be a deliberate choice, since the last changed example yields the correct result of the IF statement.
Willem Grooters
OpenVMS Developer & System Manager
Antoniov.
Honored Contributor

Re: 104.0 > 100000001: C 6.5 bug?

OK Willem,
I can't suggest no more, because my old version of cc works fine (in this case)!!
It's a bug compiler, this is sure.
Craig,
until there is a new patch, I think you could make as Willen suggested or you have to compile without /IEE=DENORM.

Also, send notice to HP.

Bye
Antoniov
Antonio Maria Vigliotti
Craig A Berry
Honored Contributor

Re: 104.0 > 100000001: C 6.5 bug?

Willem, Antoniov,

Thanks for the replies. There are certainly several ways one can force 100000001 to be a double, either at compile time or run time. The rules as I read them, though, seem to indicate the conversion should happen without any special tricks.
John Gillings
Honored Contributor

Re: 104.0 > 100000001: C 6.5 bug?

Craig,

As you're almost certainly aware, floating point arithmetic can do some very odd things. Even more so when you start playing at the fringes of the IEEE features, like denorms, NaNs and infinities.

According to the listing, your BIGNUM value is being interpreted as 0.175x10**-37


00BEBC20 0044 .LONG X^BEBC20 ; .S_FLOATING 0.1751623E-37
42D00000 0048 .LONG X^42D00000 ; .S_FLOATING 104.0000

Although I haven't worked out why, I wouldn't be too surprised to find there is a valid "language lawyer" reason, buried in an obscure paragraph of some IEEE standards document.

Do you really need /IEEE=DENORM? What do you think it's buying you?

Best practice in ANY language is to express ALL data type conversions, no matter how trivial, explicitly and unambiguously. Take care to make sure you KNOW the data type of any object and be aware of it's limitations and ideosyncrasies. For example, in most cases a test for exact equality of floating point values will be incorrect!

The vast majority of cases I see can be explained by misunderstanding the rules of the language. People are often horrified to see the complex chain of type conversions that an apparently innocuous little statement can generate. (though C is nowhere near as bad as, say, Basic in this area).

Although I'm leaning toward "bug" for this program, I'm not completely convinced. I'll run your program past the compiler engineers to see what they think.
A crucible of informative mistakes
Craig A Berry
Honored Contributor

Re: 104.0 > 100000001: C 6.5 bug?

John,

Thanks for your comments and for running this by the C folks. I think what you've posted confirms that it generates a denorm when it can't store 100000001 in a single precision float. That certainly follows the rules for denorms. So I guess this is really a question of whether the rules for comparisons using relational operators trump the rules for generating denorms in this case.

The program I posted is a greatly reduced example of a problem I encountered in porting some open source code that needs to convert various types to integer types. It needs to check whether the source of the conversion is within a range that can be represented by the target and return an error if not. It needs to do so portably, so trapping exceptions is really out of the question. So there are a bunch of macros like so, where "x" may be a real or integer of varying sizes:

#define REAL_RANGE_INT(x) ( INT_MIN <= (x) && (x) <= INT_MAX )

This works ok except when denorms are enabled, and I've enabled those because there are some places in the code that want to break the rules without generating exceptions (i.e., underflow, overflow, and such). I can work a bit harder to see if I can get away without denorms, but it's possible I can't, in which case I'll probably have to complicate the range checking logic.
Antoniov.
Honored Contributor

Re: 104.0 > 100000001: C 6.5 bug?

Hi Craig,
I my memory doesn't swindle my mind (!?) in IEEE standard NaN is applicable only to double not to single precision (but I not sure). So your code may work using double
#include
#define BIGNUM 100000001
int
main() {
double x = 104.0;
( x <= BIGNUM ?
printf("%f <= %d\n", x, BIGNUM)
: printf("%f NOT <= %d !!!\n", x, BIGNUM) );
}

I can't try because V5 works fine; try installa old version of cc.

Bye
Antoniov
Antonio Maria Vigliotti