System.Math.Round(Double, Integer) bug?

I'm not sure if I'm missing something here but there seems to be a bug in the System.Math.Round function from the .NET 2.0

For example, this code:
Math.Round(4175 /1000, 1)

Returns:
4.1999998092651367

The exact same code compiled in VB2003 (.NET 1.1) returns:
4.2

Does anyone knows why is this


Answer this question

System.Math.Round(Double, Integer) bug?

  • CurtVas

    Chris,

    Once again, thank you for the information.
    I have just submitted the bug: http://lab.msdn.microsoft.com/ProductFeedback/viewFeedback.aspx feedbackId=FDBK40230

    Perhaps you could add the information about your suspicion that this may be a problem with the Double.toString function and not directly related to the Round function.
    You are obviously more knowledgeable than me in this area and your comments might help Microsoft troubleshoot and resolve the problem.


  • TOMSUSU

    Thank you SO much for such a great explanation!
    I tried the FpuPreserve flag and it worked.

  • Holden

    Thanks for the test program. I have to admit this is some very funny behavior, and it does look like a bug.

    However, the bug is not in the Math.Round function. A Double variable cannot accurately represent a value like 0.33 so those extra digits you see are what is actually stored in there.

    The bug appears to be with the ToString method. For whatever reason, Double.ToString with no format arguments does its own output rounding before the Direct3D device has been created, but does no automatic rounding afterwards.

    I could reproduce the bug with your test program as-is, with the Visual Studio 2005 debugger, with a Visual Basic console variant, and with a C# console variant of your program.

    I suggest you file this bug with the MSDN Product Feedback website (http://lab.msdn.microsoft.com/productfeedback/). They'll be happy since you already have a nice program for reproduction!Smile

  • MaaWaa

    Floating point represenation is almost never exact. What you're seeing is simply a difference in output rounding.

    Please see Jon Skeet's article for a discussion of FP arithmetic:
    http://www.yoda.arachsys.com/csharp/floatingpoint.html

  • MgManoj

    Will do. By the way, I saw that your attachment was not uploaded -- I had the same problem once.

    You have to press the Upload button (or whatever it's called) after entering the file path and before submitting your bug report. The website does not automatically upload the attachment when you click Submit (or whatever it's called).

    I think you can still add the attachment by editing the bug report.

  • Beat

    Thanks for the explanation! So this "bug" actually originates with the Double data type and is not a bug after all. Big Smile
  • SDPointRdr

    The error you are having is caused by the floating point unit (FPU).  When a Direct3D device is created, the runtime will change the FPU to suit its needs (by default switch to single precision, the default for the CLR is double precision).  This is done because it has better performance than double precision (naturally).

    When you create a Direct3D device, the FPU is switched to single precision, and there are no longer enough digits of precision to accurately do the calculation.

    You can avoid all of this by simply telling Direct3D not to mess with the FPU at all.  When creating the device you should use the CreateFlags.FpuPreserve flag to keep the CLR's double precision, and have your code functioning as you expect it.


  • mblack

    To better illustrate this problem I have created a small application which can be used to "trigger" the bug; both binary and source code are available.
    http://software.xfx.net/ftp/RoundBug.zip

  • rmedcalf

    Chris,

    Thank you for that link. It is a very interesting reading... however, the problem I'm having with Math.Round is more serious than that caused by the limitations of handling floating point numbers.

    To test my theory (that being, that there's a serious bug with the Math.Round function) I have run some additional tests (quite simple tests):

    NOTE: Remember that the function does work fine (meaning -- it produces predictable results) before instantiating the Direct3D device. The following results are obtained right after calling this line of code:
    mDevice = New D3D.Device(0, D3D.DeviceType.Hardware, Instance, D3D.CreateFlags.MixedVertexProcessing, mPresentParams)

    Test 1: A simple rational number:
    Math.Round(3256 / 100, 2)
    Produces:
    32.560001373291016

    Test 2: A simple periodic decimal number:
    Math.Round(1 / 3, 2)
    Produces:
    0.33000001311302185

    Test 3: A complex irrational number:
    Math.Round(Math.PI, 2)
    Produces:
    3.1400001049041748

    Again, remember that the interesting thing is that the above tests return the expected results right before the Direct3D device is created and that this works just fine if the application is compiled using VS 2003

  • W.Yuan

    PS: I've looked up System.Double.ToString in Reflector. Unfortunately it's a call to a native method, so there's no immediate evidence how DirectX might influence the method. Possibly DirectX replaces floating-point handling wholesale in order to use MMX/SSE which .NET normally doesn't do

  • stefan.w

    Dear Bill,

    I'm using the final version.
    Here's some more information:

    I loaded a different project (a very simple Windows Form application) and tested the code in the immediate window and it worked.

    Then, I loaded the project where I'm having the problem (a rather complex Class Library) and the Math.Round function fails as described in my first post.

    So, here's what I did: I started debugging the application by pressing F8 and then testing the code in the immediate window; it worked.
    Kept pressing F8 going from line to line and before executing a new line I tested the code. After several hundreds lines of code I found that the error occurs right after executing this line of code:

    mDevice = New D3D.Device(0, D3D.DeviceType.Hardware, Instance, D3D.CreateFlags.MixedVertexProcessing, mPresentParams)

    If you evaluate the code before the above line of code it returns 4.2, evaluate it immediately after and it returns 4.19999...

    Interesting uh


  • Vasudevan

    xfxon,
    I'm unable to reproduce the problem you are seeing.  I'm using Visual Studio 2005 the release version.  When I run the code you reference, .NET 2.0 properly returns a 4.2.  Are you running one of the Beta/RC versions

    Thanks,
    Bill

  • System.Math.Round(Double, Integer) bug?