John,

Are you familiar with the inherent inexactness of floating-point (FP)
operations?  If not then I would recommend that you first read Bruce M.
Bush's highly informative paper "The Perils of Floating Point" at the Lahey
website: http://www.lahey.com/float.htm

Briefly, FP operations may not give the expected result (e.g. 3.0) but some
other result in the vicinity (e.g. 2.999...), and that's probably what
happened in your case.  The output functions of WriteLine and the debugger
were apparently clever enough to provide enough rounding so that a
seemingly exact value was output, but the int cast simply cut off the .9999
sequence and gave 2.0 as the result.

To see the actual representation of an FP value, instead of the rounded one
produced by most print functions, use the "round-trip" format specifier
likes this:  Console.WriteLine("Log2(8) = {0:r}", Math.Log(8,2));

This command prints 2.9999999999999996 (15 times 9) which an int cast would
correctly round to 2.  This is with .NET SP2, by the way.

That said, there does seem to be a .NET-specific problem here.  A similar
test program written in C and compiled into native code by MS VC++ .NET
gives 3.0 as the result of log(8.0)/log(2.0).  There are only zeroes after
the decimal point as far as the eye can see (I tried 30 digits; the double
type is only guaranteed to have 15 digits of precision), and casting to int
also yields 3 and not 2.

So either the Math.Log algorithm is less exact than the log function of the
C library, or the C compiler is clever enough to do some slight rounding of
its own as required -- after all, the 6 in the .NET result above came after
16 other digits and hence was past the guaranteed precision of the
System.Double type (same as double: 15 digits).  Microsoft definitely
should look into this.

In the meantime, you should either use Math.Round before casting to int, or
use Convert.ToInt32 which rounds & casts in one step.  However, both
methods will apply the bizarre "banker's rounding", i.e. rounding to even,
which Microsoft inflicted upon us for unknown reasons.  If you want real
rounding you have to write it yourself:

double d = Math.Log(8, 2);
int i = (int) (d > 0.0 ? d + 0.5 : d - 0.5);

The second line will round any FP value d to the nearest int.  The
comparison to zero is required in the general case, though not in this
example.

Cheers, Christoph

At 10:31 25.08.2002 -0700, you wrote:
>Given the following simple program:
>using System;
>public class RoundingError {
>   public static void Main(string[] args) {
>     Console.WriteLine("Log2(8) = {0}", (int) Math.Log(8,2));
>   }
>}
>
>The output is 2 if you run it standalone, but 3 (the expected value) if run
>from within the Visual Studio.NET debugger. If you print the double version
>of the return value, it is 3 in both cases...
>
>Any ideas what could be going on? I have not tried applying SP1.
>
>Thanks,
>John
>
>You can read messages from the Advanced DOTNET archive, unsubscribe from
>Advanced DOTNET, or
>subscribe to other DevelopMentor lists at http://discuss.develop.com.

You can read messages from the Advanced DOTNET archive, unsubscribe from Advanced 
DOTNET, or
subscribe to other DevelopMentor lists at http://discuss.develop.com.

Reply via email to