Answered Calculation Problem

  • Thursday, December 11, 2008 6:48 AM
     
      Has Code
    First, I have 2 values (4807.4, 4245.8), and the first value is come from a variable, another one is come from a text box, what I want to do in VB.NET is subtract these two values, it's look like very simple.
    But when I complete the coding, I find the result not my expected one, I expect I will get the 561.6, but the VB.NET return 561.59999999999945, and then I tried different calculation way, just like the following:


    1Convert.Double(4807.4) - Convert.Double(4245.8) 
    2'561.59999999999945 
    3Double.Parse(4807.4) - Convert.ToDouble(4245.8) 
    4'561.59999999999945 
    5Convert.ToDouble(4807.4) - Double.Parse(4245.8) 
    6'561.59999999999945 
    7Double.Parse(4807.4) - Double.Parse(4245.8) 
    8'561.59999999999945 
    94807.4D - Convert.ToDouble(4245.8) 
    10'561.59999999999945 
    114807.4D - Double.Parse(4245.8) 
    12'561.59999999999945 
    13Convert.ToDouble(4807.4) - 4245.8D 
    14'561.59999999999945 
    15Double.Parse(4807.4) - 4245.8D 
    16'561.59999999999945 

    What I can get the expected result is - 4807.4D - 4245.8D.

    Does it mean I need to using Decimal to store and converting all values instead of using Double?
    Moreover, when I should use double and when I should use the decimal?

Answers

  • Thursday, December 11, 2008 2:17 PM
    Moderator
     
     Answered
    Floating point arithmetic - a fundamental of computing. What you expect is not what the computer can do.

    If you are doing minimal operations then using a decimal data type should be fine. Repeatedly using decimals, however, could have a negative performance impact. Note that using decimal data type does not necessarily solve your problem: some numbers just cannot be represented correctly. The decimal data type is still susceptible to rounding errors.

    Even so, if you round 561.59999999.... to a single decimal place, you get the 'correct' answer. It depends what you will be subsequently doing with that number, but I can assure you that most operations don't need more than a 32 bit number (single). I personally very rarely need double precision in engineering calculations, let along decimals.

    But, again, it depends on what you are doing with the numbers. Decimal is most often used in financial calculations, where repeated calculations are preformed and required, such that after several million calculations, the 'correct' value is retained.



    Stephen J Whiteley
  • Thursday, December 11, 2008 6:47 PM
    Moderator
     
     Answered
    If that's the case, then a decimal is totally appropriate. It's what it's designed for. Plus, you are performing calculations extremely slowly, the performance issue is non-existent.

    A Decimal is essentially an integer number, and since you'll have exactly 2 decimal places always, and are only adding, then you'll not loose any precision.

    Go with a decimal whenever you are manipulating currency values.



    Stephen J Whiteley
  • Thursday, December 11, 2008 6:49 PM
     
     Answered
    Cerberus Wang said:

    So, is there any suggestion on how I can prevent such unexpected result? If there isn't any, what is the general practice in dealing with cases like this?

    That is the nature of floating point arithmetic in computers.  This is not a VB or .Net issue.  It occurs with most, if not all, computers.  The reason is that some floating point numbers cannot be precisely represented in binary.  Just like 1/3 cannot be precisely represented in decimal. 

    Since you are calculating monetary amount, then using the Decimal data type should suffice.  I wrote a point of sale system as well and used Decimal and did not have any problems.

    Chris
  • Thursday, December 11, 2008 7:43 PM
     
     Answered Has Code
    Hi Cerberus, I'd like to add my 2 cents in here. When working with numbers which are actually currency, I found it best to format the numbers. This allows you to do some quick rounding and manipulation of your output. With the below code you can use either formatNumber or formatCurrency, your choice. In most cases you will not want to use this until you are ready to display, though if you leave out the extra formatting and just format to the decimal places as I did below you can still use this in mathematical equations, using formatCurrency will not allow you to use the output in mathematical equations. This will return your correct calculation. I hope this is a good work around for your issue.

     

    Dim S As Single = 561.59999999999945  
     
    MessageBox.Show(FormatNumber(S, 2))  
     

     

    • Marked As Answer by Cerberus Wang Monday, December 22, 2008 2:40 AM
    •  
  • Wednesday, December 17, 2008 10:54 AM
     
     Answered
    Thank you All for your professional insight and friendly help.

    There are one thread discussing the similar issue here, you can check it for reference.
    http://social.msdn.microsoft.com/Forums/en/vbide/thread/492dea30-21cf-4ced-8579-3588d8d71c35/ 
    • Marked As Answer by Cerberus Wang Wednesday, December 17, 2008 2:17 PM
    •  

All Replies

  • Thursday, December 11, 2008 8:13 AM
     
      Has Code
    Hi Cerberus Wang,

    Create a Button1 and use this code:

    Public Class Form1  
     
        Private Sub Button1_Click(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles Button1.Click  
            Dim a As Decimal = 4807.4  
            Dim b As Decimal = Replace(TextBox1.Text, "."",", 1, Len(TextBox1.Text))  
            MsgBox(a - b)  
        End Sub 
     
        Private Sub Form1_Load(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles MyBase.Load  
            TextBox1.Text = "4245.8" 
        End Sub 
     
    End Class 


    Bill
  • Thursday, December 11, 2008 2:17 PM
    Moderator
     
     Answered
    Floating point arithmetic - a fundamental of computing. What you expect is not what the computer can do.

    If you are doing minimal operations then using a decimal data type should be fine. Repeatedly using decimals, however, could have a negative performance impact. Note that using decimal data type does not necessarily solve your problem: some numbers just cannot be represented correctly. The decimal data type is still susceptible to rounding errors.

    Even so, if you round 561.59999999.... to a single decimal place, you get the 'correct' answer. It depends what you will be subsequently doing with that number, but I can assure you that most operations don't need more than a 32 bit number (single). I personally very rarely need double precision in engineering calculations, let along decimals.

    But, again, it depends on what you are doing with the numbers. Decimal is most often used in financial calculations, where repeated calculations are preformed and required, such that after several million calculations, the 'correct' value is retained.



    Stephen J Whiteley
  • Thursday, December 11, 2008 3:32 PM
     
     

    Thanks HrBill32 and SJWhiteley first.  In fact, I'm working on a Point of Sales system, and I need to retrieve the price of the item from the user input.  Moreover, I need to calculate the total sales amount for each day, and compare that with the total cash amount they inputted. The aim is to to ensure that these 2 amounts are balanced.

    Normally, I would use the single for the price of the item, and I would use the double for the total amount of the transaction.  Moreover, I need to use decimal to calculate the sales amount for each day.  Then, just yesterday, I have encounter the problem that I’d stated in my previous postI was just frustrated, because I couldn’t find a perfect way to calculate the amount, which seems like a very simple calculation.

     

    So, is there any suggestion on how I can prevent such unexpected result? If there isn't any, what is the general practice in dealing with cases like this?

  • Thursday, December 11, 2008 6:47 PM
    Moderator
     
     Answered
    If that's the case, then a decimal is totally appropriate. It's what it's designed for. Plus, you are performing calculations extremely slowly, the performance issue is non-existent.

    A Decimal is essentially an integer number, and since you'll have exactly 2 decimal places always, and are only adding, then you'll not loose any precision.

    Go with a decimal whenever you are manipulating currency values.



    Stephen J Whiteley
  • Thursday, December 11, 2008 6:49 PM
     
     Answered
    Cerberus Wang said:

    So, is there any suggestion on how I can prevent such unexpected result? If there isn't any, what is the general practice in dealing with cases like this?

    That is the nature of floating point arithmetic in computers.  This is not a VB or .Net issue.  It occurs with most, if not all, computers.  The reason is that some floating point numbers cannot be precisely represented in binary.  Just like 1/3 cannot be precisely represented in decimal. 

    Since you are calculating monetary amount, then using the Decimal data type should suffice.  I wrote a point of sale system as well and used Decimal and did not have any problems.

    Chris
  • Thursday, December 11, 2008 7:43 PM
     
     Answered Has Code
    Hi Cerberus, I'd like to add my 2 cents in here. When working with numbers which are actually currency, I found it best to format the numbers. This allows you to do some quick rounding and manipulation of your output. With the below code you can use either formatNumber or formatCurrency, your choice. In most cases you will not want to use this until you are ready to display, though if you leave out the extra formatting and just format to the decimal places as I did below you can still use this in mathematical equations, using formatCurrency will not allow you to use the output in mathematical equations. This will return your correct calculation. I hope this is a good work around for your issue.

     

    Dim S As Single = 561.59999999999945  
     
    MessageBox.Show(FormatNumber(S, 2))  
     

     

    • Marked As Answer by Cerberus Wang Monday, December 22, 2008 2:40 AM
    •  
  • Wednesday, December 17, 2008 10:54 AM
     
     Answered
    Thank you All for your professional insight and friendly help.

    There are one thread discussing the similar issue here, you can check it for reference.
    http://social.msdn.microsoft.com/Forums/en/vbide/thread/492dea30-21cf-4ced-8579-3588d8d71c35/ 
    • Marked As Answer by Cerberus Wang Wednesday, December 17, 2008 2:17 PM
    •  
  • Wednesday, December 17, 2008 2:15 PM
     
      Has Code
            Dim natlDebt As Decimal = 57555475000000.01D 
            Dim NDasStr As String 
            NDasStr = natlDebt.ToString("C2"'format with dollar sign, comma's, and decimal point 
            Debug.WriteLine(NDasStr) 
            Stop 
     

  • Monday, December 22, 2008 2:40 AM
     
      Has Code
    Thanks JWolf011, I have tried, the formatnumber is a good work around for me.

    1Dim S As Single = 1561.59999999999945 
    2MessageBox.Show(FormatNumber(S, 2, TriState.UseDefault, TriState.UseDefault, TriState.UseDefault)) 

    • Edited by Cerberus Wang Monday, December 22, 2008 2:41 AM Change the code format
    •