Thread: Why does it run differently?

  1. #1
    Registered User
    Join Date
    Dec 2007
    Posts
    53

    Why does it run differently?

    Hey everyone. I'm a Computers Engineering student on my first semester and I'm scratching my head over an issue I'm having.

    On the current C exercises I have, two of them are compiling differently than they should, although the code seems right. And when I say differently than they should, when I compile them on the Uni's UNIX server over the net using GCC, they run perfectly. When I do the compiling using MinGW and GCC locally at home in Windows, they're faulty.

    Here is one of those programs in question:

    Code:
    #include <stdio.h>
    
    main()
    {
            float vathmoi[10], avg, max;
            int i, num, sum, count;
    
            sum = 0;
    
            for (i=0;i<=9;i++)
            {
                    do
                    {
                            printf("Enter student's grade: ", i + 1);
                            scanf("%f", &vathmoi[i]);
                            printf("%d %f\n", (int)(vathmoi[i] * 10), (vathmoi[i] * 10));
                    }
                    while ((int)(vathmoi[i] * 10) != (vathmoi[i] * 10) | vathmoi[i] < 0 | vathmoi[i] > 10);
                    sum = sum + vathmoi[i];
            }
            avg = sum / 10.0;
    
            count = 0;
            max = 0;
    
            for (i=0;i<=9;i++)
            {
                    if (vathmoi[i] >= avg) count++;
                    if (vathmoi[i] > max) max = vathmoi[i];
            }
            printf("\n\n%d students are over the average.", count);
            printf("\nMax Grade: %.1f. Students: ", max);
    
            for (i=0;i<=9;i++)
            {
                    if (vathmoi[i] == max) printf("%d ", i + 1);
            }
            printf("\n");
    }
    So, simple program, asks for 10 students' grades, and only sets them into the array if they're valid, that is, if they're between 0 and 10 and have only one digit past the decimal point. Since we're still on pretty basic stuff, the "only one digit" is determined via the multiplication by 10 and comparing the integer of that with the float. So for example, 4.6 * 10 = 46.00000 and the integer is 46, so accepted, while 4.65 * 10 = 46.50000 and the integer is 46, so denied.

    And this works perfectly if I use the uni's GCC compiler via an SSH shell. However, if I use C-Free or Code::Blocks or any other C Editor with the MinGW GCC compiler, the small debug part that prints (int)(vathmoi * 10) and (vathmoi * 10) shows that something is completely wrong!

    For example, if I give 4.6 as input for a grade, it says that (int)(vathmoi * 10) is 45 because (vathmoi * 10) is 45.999999!

    Can someone explain why this happens? Is there some basic explanation on why I can't get the program to work right on my computer, while running it over the net on the UNIX server works fine?

  2. #2
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Floating point behavior can differ between different operating systems and architectures. Floats are not infinitely precise. I don't see any real problem here.

  3. #3
    Registered User
    Join Date
    Dec 2007
    Posts
    53
    Yes, well, I understand that (to a point, that is). It's not so much of a problem, as much as it makes the whole process of debugging tedious, since I can't properly test whether my projects actually work by just compiling them on Windows. To get them to work properly, I have to transfer the file via SSH to the uni's server, compile it there and run it there, instead of just clicking Build > Run on the IDE.

    And besides boredom or tediousness or whatever, why would 4.6 be stored as 4.599999 instead of 4.600000? I mean okay, precision is not infinite, so 10.0 / 3 may equal 3.333333 and then 3.333333 * 3 = 9.999999, so okay, some precision lost there, however, why would it lose precision when just storing the number?

    This means that the program in its current form can only be run and have correct function in my uni's server, as on my computer anything but .0 or .5 is considered an invalid student's grade by the program.

    Is there anyway I can fix it or does that fall into more advanced waters that I don't want to swim in right now, being in the first semester and all?

    --------------------------------------------------

    Different Result Part. 2:

    Here's another program that compiles differently and could only be tested properly on my uni's server. This program asks the user to enter a string, inverts it, and sees if the original and the inverted string are equal. So mayyam is a string that gets a happy response, hello is not.

    Code:
    #include <stdio.h>
    #include <string.h>
    
    main()
    {
            char line[200];
            int i;
    
            gets(line);
            char linet[strlen(line)];
            char linet2[strlen(line)];
    
            strcpy(linet, line);
    
            for (i=0;i<strlen(linet);i++)
            {
                    linet2[strlen(linet)-1-i] = linet[i];
            }
            if (strcmp(linet, linet2) == 0) printf("Einai\n");
    }
    Now the problem. If compiled on my computer, "mayyam" doesn't work, "mmayyamm" doesn't either, but "mayyyam" for some weird reason, does work. On the uni's server, all those 3 above work. Any explanation for this? What is it I don't know/understand/do correctly?
    Last edited by Leftos; 12-10-2007 at 05:24 PM.

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    Read your grades in a different manner, one which doesn't involve messy floating point and the inaccuracies which result.

    Say
    int intPart, decimal;
    scanf( "&#37;d.%d", &intPart, &decimal );
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Dec 2007
    Posts
    53
    I thought of that, but wouldn't that make my life much harder while trying to get averages and maximum grade and so on?

    Funny thing is, we've been advised to use that certain comparison with the * 10 part and int by the post-graduate students that check and grade our projects. And why wouldn't they? If all work's done on the Uni's server, no such "inaccuracies" or problems occur. It's magic! *Sigh*

  6. #6
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    Quote Originally Posted by Leftos View Post
    why would 4.6 be stored as 4.599999 instead of 4.600000?
    You've hit the answer right on the head - every number can't be stored, at it's exact value. Doesn't matter WHAT you do with that number thereafter, it will be output in the future as that approximation, not the exact value.

    You can "normalize" the fp numbers before you make comparisons or output them. Google and read up to see if that's something you want to do. I've only used it a couple of times in my programs (but I just program for a hobby).
    Last edited by Adak; 12-10-2007 at 05:36 PM.

  7. #7
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by Leftos View Post
    I thought of that, but wouldn't that make my life much harder while trying to get averages and maximum grade and so on?
    Yeah, somewhat, but it's manageable. It sounds like the people who wrote this assignment don't really understand floating point inaccuracy either, and they are expecting you to do the impossible (i.e., reproduce some precise result which is not precisely reproducible by definition!)

    If you need N digits of precision, then multiply all values by 10^N, do your math in integer space, then divide by 10^N at the end.

    Just remember than when you multiply two such "scaled" values the scale factor is squared and has to be divided out. This is basically a form of fixed-point arithmetic.

    Ironically your instructors may not understand why you are doing this and dock you points for actually doing things the right way. Wouldn't be the first time.

  8. #8
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    For your string example, you don't have a '\0' at the end of your linet and linet2 (and you didn't allocate enough space for it anyway); so strcmp is going to keep examining and not necessarily see identical things off the ends of your array. printf linet and linet2 and see. (This shouldn't ever work, so you got lucky on your university machine.)

    As to the first example, floating point is floating point. It's possible that your university machine uses larger memory sizes for float, so maybe their float is your double. (I haven't checked whether 4.6 in double gives the right answer and not in float.) (I would kind-of expect both to be using IEEE standard floating point, but who knows. I would almost guarantee neither is going to use binary-coded decimal.)

  9. #9
    Registered User
    Join Date
    Dec 2007
    Posts
    53
    Thanks for all the answers, first of all. It would seem as if the university machine is magically configured to solve all these little problems. Floating point values are stored accurately and calculated accurately as well, strings do not require "null" at the end (\0) to work, and it's all fine and well.

    @tabstop: I'll try printf, then increase space by +1 and strcat \0 onto both, see what happens.

  10. #10
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by Leftos View Post
    Thanks for all the answers, first of all. It would seem as if the university machine is magically configured to solve all these little problems. Floating point values are stored accurately and calculated accurately as well, strings do not require "null" at the end (\0) to work, and it's all fine and well.
    More likely, the C library on the test machine is rounding float values before printing them, thus hiding the inaccuracy.

  11. #11
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by Leftos View Post
    Code:
            gets(line);
    No one is going to complain about gets in that code?
    Then I'm going to be the first one.
    gets is incredibly dangerous, don't use it! Use fgets instead.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  12. #12
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Right, to explain "why can't it be stored exactly", you have to understand that a floating point number is stored as binary. If we take 0.7 [1] for example:
    Each bit to the right of the decimal point is worth half as much as the previous one.
    So, we start "storing the largest portion of 0.7 that we can":
    decimal = binary
    0.5 = 0.1 // Still got 0.2 left to store
    0.5 + 0.125 = 0.101 // 0.075 left
    0.5 + 0.125 + 0.0625 = 0.1011 // 0.0125 left
    0.5 + 0.125 + 0.0625 + 0.0078125 = 0.1011001 // 0.04685 left.
    0.5 + 0.125 + 0.0625 + 0.0078125 + 0.0039075 = 0.10110011 // 0.0007775 left.

    We can continue this forever, it will never be "zero left".

    So when we convert the number back to decimal, we will only get 0.69999999.

    These numbers are not particularly rare either, so it's very likely that the conversion from decimal to binary, without even a simple calculation, will convert it to a "ends with a number of nines" situation [or ends with 0.000001].

    The RIGHT solution is to never trust floating point numbers to be precise. In the case of your check for "is there only one decimal place", you could "fudge it" by doing something like this:
    Code:
       float f;
       ...
       if ((int)(f * 10 + 0.1) - (f * 10 + 0.1) < 0.1) ....

    Oh, and don't use "|" when you actually mean "||" in your while-statement. It may not do what you want.


    [1] I picked this number as a "well-known NOT working number".
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  13. #13
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > It would seem as if the university machine is magically configured to solve all these little problems.
    Depending on your point of view, you either got lucky or unlucky.
    Personally, I think you're lucky to have the illusion of floating point infallibility so soundly smashed so early in your programming career.

    And no it hasn't solved all of them, you've just found one test case where it seems to work. There will be plenty of other cases where your local machine will seem to be correct and where both are "incorrect".

    As soon as you attempt to parse say 4.6 as a float (or a double), you're going to have to take into account the approximate nature of floating point. Simply citing "this machine produces the expected answer" is not a substitute for being right. We see it all too often, the "expected answer" is produced despite all the attempts of the author to screw up.

    Here is a nice long read.
    What Every Computer Scientist Should Know About Floating-Point Arithmetic
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  14. #14
    Registered User
    Join Date
    Dec 2007
    Posts
    53
    Once again, thanks for all the helpful answers.

    See, each project we have includes 4-6 exercises that must be completed in code. However, each project corresponds to a particular chapter(s) in Herbert Schildt's Teach Yourself C. So when I tried to calculate a power using math.h and the pow function, I was reprimanded, because, well, "you haven't been taught math.h yet." I've been programming on Visual Basic 6 for the past 5 years on my spare time, and I know there's hardly ever only one way to do something, and you can always come up with a more efficient idea.

    But since I haven't been taught math.h, I "should have" calculated the power of a certain number using loops.

    So yes, the post-graduate students that check our exercises actually told us to check the number that way, but I don't know the reason. If it doesn't work unless you have some particular edited C libraries, why would we be doing so? I'd love to test the method provided by matsp, I despise however the time it'll take me to explain to the post-graduate student something "I'm not supposed to know or care about yet", something "that's too advanced for me at this level."

    Same goes with Elysia's complaint. Of course I have wondered why every C compiler I've used complains about "gets" being very dangerous, and of course I'd love to use fgets, however we've only been taught scanf and gets, Chapter 2 and Chapter 5, while fgets is on Chapter 9, thus, no can't do mister.

    If I use it, my program may work more correctly, and I'll be teaching myself something new, however that's not the way things work for university projects.

    We haven't discussed the approximate nature of floating point numbers, because it's too advanced right now. We haven't discussed the dangers of gets in depth, because it's too advanced right now.

    Thank god we're past Hello World.
    Last edited by Leftos; 12-12-2007 at 06:06 AM. Reason: Edited some information out, exercising freedom of speech is risky.

  15. #15
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    There is of course a method in the madness in teaching you to use loops to calculate x raised to y - at least for those not used to programming. The point isn't to ACTUALLY calculate x to the power of y, but to learn how to use loops - and pow() doesn't teach you that. In some sense, it is technically "cheating" to not use loops if the purpose of the exercise is to use loops. [I'm not suggesting you did it for the purpose of cheating - I'm sure you know how to. If, however, a person wants to avoid using loops to solve the problem because he/she is unable to use loops, then that's cheating, right?].

    The same obviously applies for some other things.

    At the same time, "teaching good practices", such as "use -Wall" to produce as much warnings as possible [I'd go as far as to suggest -Wall -Werror for projects that are "school-work size"].

    Along with that, using fgets() is in itself not particularly advanced if you just teach people that "This is how you do this, ignore what it means for now":
    Code:
    fgets(str, sizeof(str), stdin)
    . It is "good practice".

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Re-doing a C program to run in Win2000 or XP
    By fifi in forum C Programming
    Replies: 5
    Last Post: 08-17-2007, 05:32 PM
  2. how to run an exe command in c++ and get back the results?
    By mitilkhatoon in forum C++ Programming
    Replies: 5
    Last Post: 09-21-2006, 06:00 PM
  3. calculating the mode
    By bigggame in forum C Programming
    Replies: 10
    Last Post: 06-13-2006, 03:04 AM
  4. How I can Run exe file in C++
    By palang in forum C++ Programming
    Replies: 2
    Last Post: 05-10-2006, 11:55 AM
  5. Replies: 2
    Last Post: 10-29-2002, 04:56 PM