Question for C pedants

Should this code work? I have tried this on two compilers, GNU and VC++ (albeit both for an Intel box) and it fails on both when no optimizations are turned on. Could other programmers check this out, and see if this happens for them?

The code is:


#include <stdio.h>

main()
{
  float a = .05f, b = .045f, c;
  c = a + b;
  if (c >= (float)(a+b)) {
    printf("Success!
");
  } else {
    printf("Failure!
");
  }
}

I understand fully about floating-point issues, but I would think that two identical calculations should come up with the exact same number, especially with the cast in the comparison.

I guess I’m pedantic enough to try it for myself but not pedantic enough to offer any real solution. I get the same results you mention (with VC++ v6.0), success in a release build but failure in a debug build.

If I leave the rest of the code unchanged, but change only “c” to type double, it works fine for me.

It’s obviously a case of floating-point precision error, as you claim. What I SUSPECT is going on (let me re-emphasize SUSPECT) is that the expression (a+b) is being promoted to a double on the stack, and that extra precision is being lost when assigned to the floating-point variable “c”.

No, what you have posted is not a C program, for the following reasons:

  1. Implicit declaration of return types is no longer permitted. That is, you should say “int main”, not “main”.

  2. The () syntax means that main has an undetermined number of arguments. While this may be fine and dandy for a function definition, it’s not for a function declaration. main must take either (void) or (int argc, char *argv) (or equivalent).

  3. You do not return from main. Returning a value from a non-void function is mandatory. Add a statement “return EXIT_SUCCESS;” or some such at the end. (You may need to include <stdlib.h> for EXIT_SUCCESS.)

A better place to ask programming questions is on comp.lang.c.

Oops… this should be vice versa. Incomplete declarations are OK, but not incomplete definitions.

Anyway, your program is still incorrect, and no conforming C compiler is obliged to make sense of it.

It’s not ANSI C. If it compiles under gcc (and it does), it is some form of C, at least K&R C.

Anyways, I couldn’t get it work under gcc or gdb either. When I evaluated the expressions, they were equal, but that comparison still failed. Guess I’ll add this to the list of reason why never to use equality comparison with non-integers.

Oh, I can tell you exactly why the code generated is failing. I’m just not sure if (a) by ANSI standards, it is legal for it to fail, or (b) is it a compiler failure or a hardware failure.

The code generated load a onto the stack, adds b to the top of the stack, and then stores the stack into c. Then it loads a onto the stack, and compares it to c.

The problem is that the values in the 387 floating-point stack are 80 bits. There is no assembly instruction to force the value in a register to a particular precision, the conversion is done during the load/store. So, should the compiler create a temporary variable on the stack, and compare c to temp, or should the processor, in the comparison, convert the register to the appropriate precision before doing the comparison?

Oh, and a lovely Bronx cheer to psychonaut for his unhelpful pedanticism, that completely fails to answer the original question. The question was not whether or not it’s a legal ANSI C program. The question was whether or not the code should always print “Success!”. FYI, the code I ran was completely valid ANSI, I just omitted those details in the OP to save some typing.

Dangit, that should read:

The code generated loads a onto the stack, adds b to the top of the stack, and then pops off the top of the stack, storing it into c. Then it loads a onto the stack, adds b to the top of the stack, and compares the top of the stack to c.

If you didn’t want a pedanticism, you shouldn’t have asked for it in your thread title. :stuck_out_tongue:

Anyway, don’t complain about my pointing out that your C program wasn’t valid. If you’re going to complain that your code doesn’t work, it’s only natural that we should point out the glaring errors first. And looking at the code generated by your program has nothing to do with C. A compiler question, maybe, but not a C question.

Oh, well in that case, I want to change my response to “Yes, the code should work just fine. I just tested it myself with Psychonaut C Complier version 0.99325gamma and it did everything I expected it to.” :rolleyes:

Do you mean to say that C programs written before ANSI are not “C programs”? What if Punoqllads wrote that piece of code in 1987? When did it become non-C?

From my copy of K&R (second edition), 1988 with a big red stamp on the cover that says “ANSI C”:


#include <stdio.h>
main ()
{
  printf("hello, world
");
}

Did Kernighan and Ritchie lose the title of C co-creators since what they originally developed is now no longer C? I’ll now go through my book with a red marker and cross off “C” and replace it with “Insufficiently C-like proto-C”.

Anyway, don’t complain about my pointing out that your 1923 Ford is not an automobile. Automobiles are now required to have seatbelts and airbags. If you’re going to complain that your automobile doesn’t run, it’s only natural that we should point out the glaring errors first. And looking under the hood has nothing to do with automobiles. An engine question, maybe, but not an automobile question.

The problem comes from the automatic promotion of floats to doubles (as first mentioned by SolomonGrundy). Specifically:

c = a + b

a and b are promoted to doubles and then added to together. The resulting temporary double is then demoted to float and stored in c.

c >= (float)(a+b)

a and b are promoted to doubles and then added together. The resulting temporary double is then demoted to float. Then, the comparison operator automatically promotes c and the temporary float to doubles and makes the comparison. The if then compares the resulting integer to 0.

It’s the comparison operator that’s forcing the second automatic promotion.

Yes, that it precisely what I mean to say. I, and most of the other denizens of comp.lang.c, take an unqualified “C” to refer to ANSI/ISO C. This convention is not snobbery but one of simple logical convenience. If we take “C” to mean “any dialect of C”, then it becomes impossible to give any definite answer to a programming question, as my sarcastic comment about the Psychonaut C Compiler demonstrated. There are scores of computing platforms, each with dozens of C compilers, each with possibly hundreds of different versions, each with dozens of proprietary extensions or unimplemented portions of the standard.

This is a terrible analogy and you know it. C, like nearly all high-level programming languages, was designed to abstract the machine code away from the user. Individual C compilers are free to translate C into machine language however they see fit, provided the resulting program conforms to the standard’s specifications. Therefore, unless one suspects a compiler bug, nothing can be gained by examining the binary; the binary is going to be different with each platform, compiler, and possibly even version of compiler. But if the issue is a compiler bug, this is not a C question but a C compiler question, and “C pedants” are not necessarily going to be able to help.

If you want a better automobile analogy, let’s say the OP is complaining that he is having trouble driving his “car”, but does not specify which make and model he has. The diagnostic information he gives shows that he has simply inserted the wrong key into the starter, yet he insists on lifting up the hood and looking for engine trouble. He could very well have engine trouble, but the more obvious error is that he used the wrong key. Even if he does have engine trouble, “car experts” are not in a position to help him (without more information) since there is no generic solution that fixes engine trouble on any given car.

Could I just visibly and publicly remove myself from this thread? At first I thought “Punoqllads” was being obnoxious for pointing out “glaring errors” in the original post, but then I read “psychonaut’s” reply that he knew the code wasn’t working and why it wasn’t working, so I’m even more baffled now as to why I bothered to respond in the first place.

This is exactly why I try not to talk to other programmers.

psychonaut,

a) you’re wrong
b) you’re irrelevant
c) you’re being laughed at by people who are far more knowledgable on the subject than you

Uh, might you have psychonaut and I confused with each other?

To clarify my OP, yes, I know why it doesn’t work on the machines I’ve compiled it for. What I was wondering was if other folks thought that there was a reasonable explanation for why it might not be a compiler bug (considering I found it in two different compilers) and whether or not others could replicate this behavior on other platforms.

Bill H.,

Saying someone is wrong does not make him so. I have been very careful in defending with reason all the points I have made. You have contributed nothing to this thread save an ineffectual, improperly-spelled taunt.

If you disagree with something I’ve said, you should indicate what and why. Otherwise the only one who’s sure to be making a fool of himself is you.

Yes, but I’ve only wasted 4 lines (well 5 now I guess) to do it. If you honestly don’t get my comments, there’s no point in my wasting further training time on you.

Now now, I’m certainly not laughing, and I wouldn’t bet I’m more knowledgeable than psychonaut. My main problem is I stopped coding in C/C++ several years ago, and the last time I did so it involved writing code that could be compiled on 5 kajillion different platforms including some without ANSI compilers.

I disagree with psychonaut, but I understand his position.

It is still my position that old C code is still C, but I think psychonaut’s position is clear.