Why there are two diffrent result in two almost same formula

  • Thread starter Thread starter XjAcKs
  • Start date Start date
X

XjAcKs

Hi everyone. My source code is the following in VS2005:

--------
static void Main(string[] args)
{
int num = 3600;
Single XNT = 0.3F;
Single SX = 1546.08594F;

Single result = (XNT * num) / SX;
Single result2 = System.Convert.ToSingle(XNT * num) / SX;

System.Console.WriteLine(result);
System.Console.WriteLine(result2);
return;
}
--------

The result is "0.6985382", but the result2 is "0.6985381".
Why there are two diffrent result here?

When "XNT * num", because "XNT" is the type of Single, I think the system should
automatic convert the result to Single. But when I manually add the
"System.Convert.ToSingle()", the diffrent result appeared. Any idea?

Thank you!
 
It happens that XjAcKs formulated :
Hi everyone. My source code is the following in VS2005:

--------
static void Main(string[] args)
{
int num = 3600;
Single XNT = 0.3F;
Single SX = 1546.08594F;

Single result = (XNT * num) / SX;
Single result2 = System.Convert.ToSingle(XNT * num) / SX;

System.Console.WriteLine(result);
System.Console.WriteLine(result2);
return;
}
--------

The result is "0.6985382", but the result2 is "0.6985381".
Why there are two diffrent result here?

When "XNT * num", because "XNT" is the type of Single, I think the system
should automatic convert the result to Single. But when I manually add the
"System.Convert.ToSingle()", the diffrent result appeared. Any idea?

Thank you!

Want even more scary things? Try this:

public class MyClass
{
static float f;

static float Sum (float f1, float f2)
{
return f1+f2;
}

public static void Main()
{
f = Sum (0.1f, 0.2f);
float g = Sum (0.1f, 0.2f);
Console.WriteLine (f==g);
// uncomment the next line and run again:
//Console.WriteLine (f==g);
// or use only this writeline:
//Console.WriteLine (f==(float)g);
Console.ReadKey();
}
}

Apparently the compiler can decide to keep float variables as regular
stack variables or as register variables. Those latter ones exist only
in the CPU and are stored in a different precision (80 bits as opposed
to 64 if I remember correctly). And the 80 bit representation of some
number will be different from the 64 bits version ...

Hans Kesting
 
It happens that XjAcKs formulated :
Hi everyone. My source code is the following in VS2005:

--------
static void Main(string[] args)
{
int num = 3600;
Single XNT = 0.3F;
Single SX = 1546.08594F;

Single result = (XNT * num) / SX;
Single result2 = System.Convert.ToSingle(XNT * num) / SX;

System.Console.WriteLine(result);
System.Console.WriteLine(result2);
return;
}
--------

The result is "0.6985382", but the result2 is "0.6985381".
Why there are two diffrent result here?

When "XNT * num", because "XNT" is the type of Single, I think the
system should automatic convert the result to Single. But when I
manually add the
"System.Convert.ToSingle()", the diffrent result appeared. Any idea?

Thank you!

Want even more scary things? Try this:

public class MyClass
{
static float f;

static float Sum (float f1, float f2)
{
return f1+f2;
}

public static void Main()
{
f = Sum (0.1f, 0.2f);
float g = Sum (0.1f, 0.2f);
Console.WriteLine (f==g);
// uncomment the next line and run again:
//Console.WriteLine (f==g);
// or use only this writeline:
//Console.WriteLine (f==(float)g);
Console.ReadKey();
}
}

Apparently the compiler can decide to keep float variables as regular
stack variables or as register variables. Those latter ones exist only
in the CPU and are stored in a different precision (80 bits as opposed
to 64 if I remember correctly). And the 80 bit representation of some
number will be different from the 64 bits version ...

Hans Kesting

Thanks for you reply. I read your reply and understand your meaning. But, I
haven't know how it happend in my program yet. I think the variables in my
program are all kept as regular stack variables because there are no "static"
defined...

XjAcKs
 
[...]
When "XNT * num", because "XNT" is the type of Single, I think the
system should
automatic convert the result to Single. But when I manually add the
"System.Convert.ToSingle()", the diffrent result appeared. Any idea?

Thank you!

Additional:

I just changed the variable "num" from int to Single. But I was
shocked that the
two result is also diffrent.
(XNT * num) / SX -> 0.6985382
System.Convert.ToSingle(XNT * num) / SX -> 0.6985381

How? It confuse me very much...

I haven't had a chance to verify your results. I do agree that,
assuming the code actually does what you say it does, there's something
odd going on.

...

Finally, note that if it is a bug, it's possible it's been fixed since
.NET 2.0/VS 2005 (depending on whether it's a framework or compiler
bug). I'm only going to be able to test the code with .NET 3.5/VS 2008
at the moment, so one thing you might want to do is try to test it
yourself on a more recent compiler and/or .NET version and see if it
still happens. That would be a very useful data point.

Pete

Thanks for you reply. My english is poor, so I will have to read your reply for
a while. If I find out something later, I will send a reply.

BTW: I just try the program in .NET 3.5/VS 2008, and the result the same as .NET
2.0/VS 2005, so I think it seems not a complier bug or it haven't been fixed.
 
[...]
When "XNT * num", because "XNT" is the type of Single, I think the
system should
automatic convert the result to Single. But when I manually add the
"System.Convert.ToSingle()", the diffrent result appeared. Any idea?

Thank you!

Additional:

I just changed the variable "num" from int to Single. But I was
shocked that the
two result is also diffrent.
(XNT * num) / SX -> 0.6985382
System.Convert.ToSingle(XNT * num) / SX -> 0.6985381

How? It confuse me very much...

I haven't had a chance to verify your results. I do agree that,
assuming the code actually does what you say it does, there's
something odd going on.

...

Finally, note that if it is a bug, it's possible it's been fixed since
.NET 2.0/VS 2005 (depending on whether it's a framework or compiler
bug). I'm only going to be able to test the code with .NET 3.5/VS
2008 at the moment, so one thing you might want to do is try to test
it yourself on a more recent compiler and/or .NET version and see if
it still happens. That would be a very useful data point.

Pete

Thanks for you reply. My english is poor, so I will have to read your
reply for a while. If I find out something later, I will send a reply.

BTW: I just try the program in .NET 3.5/VS 2008, and the result the same
as .NET 2.0/VS 2005, so I think it seems not a complier bug or it
haven't been fixed.

I just try my code in Java language, and the expression's result is the same
value: 0.6985381. So I think the expression with ToSingle() method is right...
 
Thanks for you reply. My english is poor, so I will have to read your
I just try my code in Java language, and the expression's result is the
same value: 0.6985381. So I think the expression with ToSingle() method
is right...

I think I have figured out out.
I changed the variables definition "result" and "result2" to Double and I delete
the "SX" in expression. And it is :

int num = 3600;
Single XNT = 0.3F;
Double result = XNT * num ;
Double result2 = System.Convert.ToSingle(XNT * num);

And, the value of two result is:
result -> 1080.00004291534
result2 -> 1080

It seems that Single is more better than Double here...
 
Thanks for you reply. My english is poor, so I will have to read your
I think I have figured out out.
I changed the variables definition "result" and "result2" to Double and
I delete the "SX" in expression. And it is :

int num = 3600;
Single XNT = 0.3F;
Double result = XNT * num ;
Double result2 = System.Convert.ToSingle(XNT * num);

And, the value of two result is:
result -> 1080.00004291534
result2 -> 1080

It seems that Single is more better than Double here...

Single result = (XNT * num) / SX;
Single result2 = System.Convert.ToSingle(XNT * num) / SX;

So I think in the first expression, the result of "XNT * num" is a type of Double.

But why one Single variable multiply another Single variable, the result is a
type of Double? How weird it is...
 
What happens if you change

Single result2 = System.Convert.ToSingle(XNT * num) / SX;

to

Single result2 = System.Convert.ToSingle((XNT * num) / SX);


?

First one, you are modifying the left side before dividing. Second one, you
are modifying the result. I would suggest the second one would be correct.

--
Best regards,
Dave Colliver.
http://www.AshfieldFOCUS.com
~~
http://www.FOCUSPortals.com - Local franchises available
 
XjAcKs wrote :
It happens that XjAcKs formulated :
Hi everyone. My source code is the following in VS2005:

--------
static void Main(string[] args)
{
int num = 3600;
Single XNT = 0.3F;
Single SX = 1546.08594F;

Single result = (XNT * num) / SX;
Single result2 = System.Convert.ToSingle(XNT * num) / SX;

System.Console.WriteLine(result);
System.Console.WriteLine(result2);
return;
}
--------

The result is "0.6985382", but the result2 is "0.6985381".
Why there are two diffrent result here?

When "XNT * num", because "XNT" is the type of Single, I think the system
should automatic convert the result to Single. But when I manually add the
"System.Convert.ToSingle()", the diffrent result appeared. Any idea?

Thank you!

Want even more scary things? Try this:

public class MyClass
{
static float f;

static float Sum (float f1, float f2)
{
return f1+f2;
}

public static void Main()
{
f = Sum (0.1f, 0.2f);
float g = Sum (0.1f, 0.2f);
Console.WriteLine (f==g);
// uncomment the next line and run again:
//Console.WriteLine (f==g);
// or use only this writeline:
//Console.WriteLine (f==(float)g);
Console.ReadKey();
}
}

Apparently the compiler can decide to keep float variables as regular stack
variables or as register variables. Those latter ones exist only in the CPU
and are stored in a different precision (80 bits as opposed to 64 if I
remember correctly). And the 80 bit representation of some number will be
different from the 64 bits version ...

Hans Kesting

Thanks for you reply. I read your reply and understand your meaning. But, I
haven't know how it happend in my program yet. I think the variables in my
program are all kept as regular stack variables because there are no "static"
defined...

XjAcKs

It doesn't have to be a static variable. It's just that the compiler
may decide to have some variables as 64 bit stack variables while
others (the short-lived ones I presume) can be 80 bit register
variables.
But I don't understand the workings of the compiler enough to make an
example without the static variable.

Hans Kesting
 
What happens if you change
Single result2 = System.Convert.ToSingle(XNT * num) / SX;

to

Single result2 = System.Convert.ToSingle((XNT * num) / SX);


?

First one, you are modifying the left side before dividing. Second one, you
are modifying the result. I would suggest the second one would be correct.

After did the change you said, the result is "0.6985382", the same value as the
expression without ToSingle().

But if I use calculator program of WindowsXP and manually input the formula
"(0.3 * 3600) / 1546.08594", the result is "0.69853814206472895031954045193633",
so I think it is correct modifying the left side before dividing by ToSingle().
 
That depends on what you mean by "is a type of Double".
Looking at the replies from Hans, I suspect that the difference can be
explained by the compiler using storage of precision Double to do the
calculation. But, the expression is most definitely _not_ literally
typed as Double; there are very specific type conversion rules in C#
that would preclude treating the expression that way.


As Hans says, it may be more efficient to do the calculation using a
higher-precision storage, effectively promoting the values to Double
temporarily during the calculation. Because the semantic type of the
expression remains Single though, the compiler will wind up converting
the result back to Single when storing it.

In the code you posted, assuming this behavior (I still don't have time
to go check everything myself), that would readily explain why you get
different results depending on at what step you force the conversion
back to Single. When you call Convert.ToSingle(), that forces the
conversion to happen immediately after the multiplication. When you
don't, then the conversion can be delayed until after the division. The
potential for that to produce different results is obvious.

Pete

By do some experiment and read your reply, finally I understood the reason why
the problem happend.

Pete, Hans, Thank you very much! It's very helpful.

XjAcKs
 
XjAcKs wrote :
It happens that XjAcKs formulated :
Hi everyone. My source code is the following in VS2005:

--------
static void Main(string[] args)
{
int num = 3600;
Single XNT = 0.3F;
Single SX = 1546.08594F;

Single result = (XNT * num) / SX;
Single result2 = System.Convert.ToSingle(XNT * num) / SX;

System.Console.WriteLine(result);
System.Console.WriteLine(result2);
return;
}
--------

The result is "0.6985382", but the result2 is "0.6985381".
Why there are two diffrent result here?

When "XNT * num", because "XNT" is the type of Single, I think the
system should automatic convert the result to Single. But when I
manually add the
"System.Convert.ToSingle()", the diffrent result appeared. Any idea?

Thank you!

Want even more scary things? Try this:

public class MyClass
{
static float f;

static float Sum (float f1, float f2)
{
return f1+f2;
}

public static void Main()
{
f = Sum (0.1f, 0.2f);
float g = Sum (0.1f, 0.2f);
Console.WriteLine (f==g);
// uncomment the next line and run again:
//Console.WriteLine (f==g);
// or use only this writeline:
//Console.WriteLine (f==(float)g);
Console.ReadKey();
}
}

Apparently the compiler can decide to keep float variables as regular
stack variables or as register variables. Those latter ones exist
only in the CPU and are stored in a different precision (80 bits as
opposed to 64 if I remember correctly). And the 80 bit representation
of some number will be different from the 64 bits version ...

Hans Kesting

Thanks for you reply. I read your reply and understand your meaning.
But, I haven't know how it happend in my program yet. I think the
variables in my program are all kept as regular stack variables
because there are no "static" defined...

XjAcKs

It doesn't have to be a static variable. It's just that the compiler may
decide to have some variables as 64 bit stack variables while others
(the short-lived ones I presume) can be 80 bit register variables.
But I don't understand the workings of the compiler enough to make an
example without the static variable.

Hans Kesting

OK, I will remember it. Thank you, Hans.

XjAcKs
 
Hi XjAcKs,

The precision for Single (float) type is 7 digits (you are using more
than that). If you change SX to 1546.086F, the program will behave
correctly.
If you need more precision, use double.

Regards.
 
Hi everyone. My source code is the following in VS2005:

--------
        static void Main(string[] args)
        {
            int num = 3600;
            Single XNT = 0.3F;
            Single SX = 1546.08594F;

            Single result = (XNT * num) / SX;
            Single result2 = System.Convert.ToSingle(XNT * num) / SX;

            System.Console.WriteLine(result);
            System.Console.WriteLine(result2);
            return;
        }
--------

The result is "0.6985382", but the result2 is "0.6985381".
Why there are two diffrent result here?

When "XNT * num", because "XNT" is the type of Single, I think the systemshould
automatic convert the result to Single. But when I manually add the
"System.Convert.ToSingle()", the diffrent result appeared. Any idea?

Thank you!

On Intel processors, Single is first converted into an 80-bit
precision floating-point before calculation.

The statement
Single result = (XNT * num) / SX;
would be doing an 80-bit XNT * 80-bit num / 80-bit SX, then converting
it back to 32-bit Single.

The statement
Single result2 = System.Convert.ToSingle(XNT * num) / SX;
would instead be
80-bit XNT * 80-bit num, then convert to Single
Pass the 32-bit Single into System.Convert.ToSingle
Then convert the result into 80-bit and divide by 80-bit SX, then
converting back to Single.

The 2nd case would have introduced more errors.

BTW, you might consider using Double since Intel processors calculate
Double directly (no conversion into 80-bit floating point), and so
should run faster.
 
I think I have figured out out.
I changed the variables definition "result" and "result2" to Double and Idelete
the "SX" in expression. And it is :

int num = 3600;
Single XNT = 0.3F;
Double result = XNT * num ;
Double result2 = System.Convert.ToSingle(XNT * num);

And, the value of two result is:
result -> 1080.00004291534
result2 -> 1080

It seems that Single is more better than Double here...

I believe this is a wrong conclusion.

int num = 3600;
Single XNT = 0.3F;
Double result = XNT * num ;

1. num is converted into an 80-bit floating point.
2. XNT is converted into an 80-bit floating point.
Since 0.3F cannot be represented precisely by a Single, the 80-bit
floating point will similarly be imprecise.
3. (80-bit)XNT * (80-bit)num results in an imprecise answer due to the
previous step.
4. The imprecise result in step 3 is stored into a Double. Since
double shows more decimal point then Single, the imprecise part is
shown.



int num = 3600;
Single XNT = 0.3F;
Double result2 = System.Convert.ToSingle(XNT * num);

Steps 1 to 3 are the same as previous.

1. num is converted into an 80-bit floating point.
2. XNT is converted into an 80-bit floating point.
Since 0.3F cannot be represented precisely by a Single, the 80-bit
floating point will similarly be imprecise.
3. (80-bit)XNT * (80-bit)num results in an imprecise answer due to the
previous step.
4. The imprecise result in step 3 is converted into Single, truncating
the imprecise part.
5. The truncated result from step 4 is converted into Double, and
since 1080 can be stored precisely in a Single, converting it to
Double gives a precise result.

The conclusion will be that the 1st result appears imprecise due to
the imprecise 0.3F to start with.
 
Hi Li Huan,

Using SAME Intel machine, the expression

(XNT * num) / SX

will return
0.6985382 on .NET
0.6985381 on JAVA

Can you explain this behaviour?
Maybe someone who have Mono and/or Java on Linux machine can post their
result here...

Li said:
Hi everyone. My source code is the following in VS2005:

--------
static void Main(string[] args)
{
int num = 3600;
Single XNT = 0.3F;
Single SX = 1546.08594F;

Single result = (XNT * num) / SX;
Single result2 = System.Convert.ToSingle(XNT * num) / SX;

System.Console.WriteLine(result);
System.Console.WriteLine(result2);
return;
}
--------

The result is "0.6985382", but the result2 is "0.6985381".
Why there are two diffrent result here?

When "XNT * num", because "XNT" is the type of Single, I think the system should
automatic convert the result to Single. But when I manually add the
"System.Convert.ToSingle()", the diffrent result appeared. Any idea?

Thank you!

On Intel processors, Single is first converted into an 80-bit
precision floating-point before calculation.

The statement
Single result = (XNT * num) / SX;
would be doing an 80-bit XNT * 80-bit num / 80-bit SX, then converting
it back to 32-bit Single.

The statement
Single result2 = System.Convert.ToSingle(XNT * num) / SX;
would instead be
80-bit XNT * 80-bit num, then convert to Single
Pass the 32-bit Single into System.Convert.ToSingle
Then convert the result into 80-bit and divide by 80-bit SX, then
converting back to Single.

The 2nd case would have introduced more errors.

BTW, you might consider using Double since Intel processors calculate
Double directly (no conversion into 80-bit floating point), and so
should run faster.
 
I believe this is a wrong conclusion.
int num = 3600;
Single XNT = 0.3F;
Double result = XNT * num ;

1. num is converted into an 80-bit floating point.
2. XNT is converted into an 80-bit floating point.
Since 0.3F cannot be represented precisely by a Single, the 80-bit
floating point will similarly be imprecise.
3. (80-bit)XNT * (80-bit)num results in an imprecise answer due to the
previous step.
4. The imprecise result in step 3 is stored into a Double. Since
double shows more decimal point then Single, the imprecise part is
shown.



int num = 3600;
Single XNT = 0.3F;
Double result2 = System.Convert.ToSingle(XNT * num);

Steps 1 to 3 are the same as previous.

1. num is converted into an 80-bit floating point.
2. XNT is converted into an 80-bit floating point.
Since 0.3F cannot be represented precisely by a Single, the 80-bit
floating point will similarly be imprecise.
3. (80-bit)XNT * (80-bit)num results in an imprecise answer due to the
previous step.
4. The imprecise result in step 3 is converted into Single, truncating
the imprecise part.
5. The truncated result from step 4 is converted into Double, and
since 1080 can be stored precisely in a Single, converting it to
Double gives a precise result.

The conclusion will be that the 1st result appears imprecise due to
the imprecise 0.3F to start with.

I think you spoke to the point. It is very helpful for me.
Thank you!
 
Hi kndg,

Thank you for you advise. But in my program, "1546.08594F" has some meaning and
can't change it to 1546.086F.
I think it is better to use Double instead of Snigle.

Hi XjAcKs,

The precision for Single (float) type is 7 digits (you are using more
than that). If you change SX to 1546.086F, the program will behave
correctly.
If you need more precision, use double.

Regards.
Hi everyone. My source code is the following in VS2005:

--------
static void Main(string[] args)
{
int num = 3600;
Single XNT = 0.3F;
Single SX = 1546.08594F;

Single result = (XNT * num) / SX;
Single result2 = System.Convert.ToSingle(XNT * num) / SX;

System.Console.WriteLine(result);
System.Console.WriteLine(result2);
return;
}
--------

The result is "0.6985382", but the result2 is "0.6985381".
Why there are two diffrent result here?

When "XNT * num", because "XNT" is the type of Single, I think the system should
automatic convert the result to Single. But when I manually add the
"System.Convert.ToSingle()", the diffrent result appeared. Any idea?

Thank you!
 
Finally, you should keep in mind that this entire message thread is
purely academic curiosity. If you have some program requirement that
calculations produce exact results, then neither Single/float nor
Double/double are appropriate. That's just not something those floating
point types provide.

Pete

I am curious to know if I have a program requirement that calculations produce
exact results, which should I use?
 
XjAcKs said:
I am curious to know if I have a program requirement that calculations produce
exact results, which should I use?

None of the above. Floating point arithmetic NEVER produces exact results.
That's just the nature of floating point arithmetic. It's an
approximation.

You've already seen this. You wrote the number 1546.08594, but that number
**CANNOT** be represented exactly as a floating point value. In binary,
that number is an infinitely repeating fraction. It doesn't matter how
many bits you use, the floating point representation will ALWAYS be an
approximation. It's exactly the same as if I asked you to write down the
EXACT decimal representation of 1/3. It can't be done.

However, that's OK, because the number 1546.08594 is itself obviously an
approximation. The KEY QUESTION you need to ask is, "how many digits/bits
of precision do I have to begin with?" If that number was really measured
to 9 digits of accuracy (which is highly doubtful), then you might need to
consider doubles. They maintain about 15 digits.

Further, every computation you do reduces the accuracy more and more. There
are plenty of books and web sites that describe numerical analysis with
floating point numbers. You just need to be aware.
 
Back
Top