Strange result casting float to long

Hi all,

I'm working with VC++6.0 in WinNT4 SP4 on a Pentium II 300. With following code I get strange results when casting the result of a floating point operation to a long.

This is my code:
--------------------------------
#include <stdio.h>
#include <math.h>

void func1()
{
 float x1, x2, x3;
 long res;

 x1 = 23.8f;
 x2 = 100.0f;
 res = (long)(x1 * x2);
 printf( "Result : %i.\n", res );
 x3 = x1 * x2;
 res = (long)x3;
 printf( "Result : %i.\n", res );
}

void func2()
{
 float x1, x2, x3;
 long res;

 x1 = 23.8f;
 x2 = 100.0f;
 res = (long)(x1 * x2);
 x3 = x1 * x2;                       // same function as func1 except these
 printf( "Result : %i.\n", res );    // two lines are in different order.
 res = (long)x3;
 printf( "Result : %i.\n", res );
}

int main(int argc, char* argv[])
{
 printf("Output of func1 : \n");
 func1();
 printf("Output of func2 : \n");
 func2();
 return 0;
}
--------------------------------

This is the output:
--------------------------------
Output of func1 :
Result : 2379.
Result : 2379.
Output of func2 :
Result : 2379.
Result : 2380.
--------------------------------

My Questions:

Why does 'res=(long)(x1*x2);' differ from 'x3=x1*x2,res=(long)x3' in func2() and why does func1() produce a different output than func2() ???

any help appreciated,

ZOPPO

LVL 32
ZoppoAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

nietodCommented:
>> Why does 'res=(long)x1*x2;' differ
>> from 'x3=x1*x2,res=(long)x3'
To the best f my knowledge there should be no difference.  (Assuming you have the parenthesis correct, which you do in the code, but not in you comment).

>> why does func1() produce a different
>> output than func2()
It shouldn't.  I don't have time now, but I will try this later and see if I get the same results.
ZoppoAuthor Commented:
Edited text of question.
ZoppoAuthor Commented:
Hi nietod, I would agree with your comments, but results say they're wrong. Nevertheless thanks for proofreading ;-)

ZOPPO
HTML5 and CSS3 Fundamentals

Build a website from the ground up by first learning the fundamentals of HTML5 and CSS3, the two popular programming languages used to present content online. HTML deals with fonts, colors, graphics, and hyperlinks, while CSS describes how HTML elements are to be displayed.

rbrCommented:
if the output is
To different problems.

1.) Why 2 different results( 2379,2380). This is easy.
All operation in C are double. So the multiplication leads to
2379.999924. Cast to long this is 2379. Casted to float this is 2380 and casted this ti long is 2380.
2.) Why 2 different results in the 2 functions. Strange. My compiler (linux) put 2379 and 2380 out in both functions. Could be an omptimization problem. What will be the results if the turn off optimazation.

rbrCommented:
Id you use double res instead of float res the output will be 2379 instead of 2380.
rbrCommented:
Sorry not double res, use double x3.
ZoppoAuthor Commented:
Hi rbr,

sorry me for rejecting your answer, but I'd like some more details:

1. I thought C does only cast a float to double if any value of the expression is of type double. Isn't it a very bad (for performance) strategy to cast any float to double before calculating anything? From where did you get the information that any C operation is double?

2. No optimation is used to compile this code! If someone can answer me this I'll grade the answer with A.

Thanks,

ZOPPO
rbrCommented:
Yes that^s true. All operation are double so double are faster than float (only if you have less memory use float). Did you ever see any C function which use float (e.g. atof returns a double not a float) I had these info out of a C book, long time ago, but i can^t remember the title. Why the functions have different results I think this must be a compiler problem (error). Which compiler and os to you use. Have you tried the double?
ZoppoAuthor Commented:
Hi rbr,

excuse me for rejecting again, just one more comment:

yes, I've tried it with the double and so far you're right. But if your description is correct, the expression 'res = (long)( (float)( x1 * x2 ) )' should return 2380 because of casting double to float and then to long, but it returns also 2379. Why this?

OS is WinNT4 SP4, compiler is VisualC++ 6.0 SR2. I didn't find anything about such an error in the knowledge base or on a web site yet.

ZOPPO
ZoppoAuthor Commented:
Forgot to check the reject option ...
rbrCommented:
(long)((float)) produces the same assembler code in as (long) allone with my compiler. It looks like the compiler ignors it.
ZoppoAuthor Commented:
You're right again. Now I begin to guess why i.e. Excel produces sometimes really strange results when simply adding some products. Seems it's better to use double generally!

You'll get the points when you post an answer, but I'm still interested if anyone else can tell something about 2.), so I'll wait till tomorrow before I grade soemthing.
Hope you agree...

ZOPPO
viktornetCommented:
I tried your code and got the following results...

Output of func1 :
Result : 2379.
Result : 2380.
Output of func2 :
Result : 2379.
Result : 2380.

I used TC++ on a Win95 OS...

Both results are supposed to be the same as you can see.... I dunno why it changes, probably some kind of optimization as rbr has previously mentioned...

'..-=ViKtOr=-..'
Roger AlcindorCommented:
Your printf statements are incorrect. The format specifier    %i   should in fact be %l   ( lower case letter L ) for a long type argument, %i is for integer arguments. I'm not saying that this explains anything but if it fixes your problem then all well & good !

Roger
basantCommented:
Dear Zoppo,
   I tried with ur program. I faced the same result
as yours. By merely changing the order of print statement
the result changed.
I myself is surprised with the result.
ZoppoAuthor Commented:
Hi alcindor,

sorry, but %l is not an available option. Available are %i and %d for signed and %u for unsigned integers. Especially for long there's a size prefix 'l' for integer types, i.e. %ld for a signed long. But this does not change anything; the output is yet the same as with %i.

Viktornet, I also don't know, but it's surely no optimization problem. Optimizations are all turned off.

ZOPPO
rbrCommented:
Would you give to points to me now?
Thx.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
ZoppoAuthor Commented:
Of course, just waited for your answer :-}

have a nice day

ZOPPO

nietodCommented:
Here is the reason for the difference.  Its just due to the fact that you are using too short a precising and that floating point is really not all that perfect.  

When you do the math and convert to a long with out the printf(), then the compiler uses the 80 bit floating point stored in the FPU as the number to convert to the long rather than the 32 bit number stored in res, the code used is

16:    x3 = x1 * x2;
00401136   fld         dword ptr [ebp-4]    // Get X1.
00401139   fmul        dword ptr [ebp-8]   // Multiply by X2.
0040113C   fst         dword ptr [ebp-0Ch]  // Store in res and round to 32 bits.
17:    res = (long)x3;
0040113F   call        __ftol (0041e10c) // Convert the 80 bit number to long.

When you have the printf, it can't convert the 80 bit number to the long so it ends up converting the 32 bit number as in

28:    res = (long)(x1 * x2);
00401177   fld         dword ptr [ebp-4]    // Get x1
00401188   fmul        dword ptr [ebp-8]  // Multiply by x2.
0040118B   fstp        dword ptr [ebp-0Ch]  // store as 32 bits in res.
// Now the printf occurs.

31:    res = (long)x3;
0040119F   fld         dword ptr [ebp-0Ch] // Load the 32 bit res.
004011A2   call        __ftol (0041e10c) // Covert to long.

So the difference is that when you have the intervening printf(), the result is converted to 32 bit floating point before converting to long. Otherwise the more accurate 80 bit floating point is converted to long.
ZoppoAuthor Commented:
Hi nietod,

thanks a lot for your comment. Good explanation. I think I understand it although I'm a real assembler-greenhorn. It's sometimes horrible to what developers must pay attention.

Now, as I told previously, you'll get the points for this, too.

Post an empty answer for question http://www.experts-exchange.com/Computers/Programming/Windows/Q.10169739 and I'll grade it with A.

have a nice time,

ZOPPO
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Microsoft Development

From novice to tech pro — start learning today.