Link to home
Start Free TrialLog in
Avatar of Zoppo
ZoppoFlag for Germany

asked on

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

Avatar of nietod
nietod

>> 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.
Avatar of Zoppo

ASKER

Edited text of question.
Avatar of Zoppo

ASKER

Hi nietod, I would agree with your comments, but results say they're wrong. Nevertheless thanks for proofreading ;-)

ZOPPO
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.

Id you use double res instead of float res the output will be 2379 instead of 2380.
Sorry not double res, use double x3.
Avatar of Zoppo

ASKER

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
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?
Avatar of Zoppo

ASKER

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
Avatar of Zoppo

ASKER

Forgot to check the reject option ...
(long)((float)) produces the same assembler code in as (long) allone with my compiler. It looks like the compiler ignors it.
Avatar of Zoppo

ASKER

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
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=-..'
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
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.
Avatar of Zoppo

ASKER

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
ASKER CERTIFIED SOLUTION
Avatar of rbr
rbr

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Zoppo

ASKER

Of course, just waited for your answer :-}

have a nice day

ZOPPO

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.
Avatar of Zoppo

ASKER

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 https://www.experts-exchange.com/Computers/Programming/Windows/Q.10169739 and I'll grade it with A.

have a nice time,

ZOPPO