Calculation Problem
-
Thursday, December 11, 2008 6:48 AM
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:1 Convert.Double(4807.4) - Convert.Double(4245.8) 2 '561.59999999999945 3 Double.Parse(4807.4) - Convert.ToDouble(4245.8) 4 '561.59999999999945 5 Convert.ToDouble(4807.4) - Double.Parse(4245.8) 6 '561.59999999999945 7 Double.Parse(4807.4) - Double.Parse(4245.8) 8 '561.59999999999945 9 4807.4D - Convert.ToDouble(4245.8) 10 '561.59999999999945 11 4807.4D - Double.Parse(4245.8) 12 '561.59999999999945 13 Convert.ToDouble(4807.4) - 4245.8D 14 '561.59999999999945 15 Double.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 PMModerator
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- Marked As Answer by Martin Xie - MSFT Wednesday, December 17, 2008 10:43 AM
-
Thursday, December 11, 2008 6:47 PMModerator
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- Marked As Answer by Martin Xie - MSFT Wednesday, December 17, 2008 10:46 AM
-
Thursday, December 11, 2008 6:49 PM
Cerberus Wang said: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.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?
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- Marked As Answer by Martin Xie - MSFT Wednesday, December 17, 2008 10:45 AM
-
Thursday, December 11, 2008 7:43 PM
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
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
Hi Cerberus Wang,
Create a Button1 and use this code:
Public Class Form1 Private Sub Button1_Click(ByVal sender As System.Object, ByVal 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.Object, ByVal e As System.EventArgs) Handles MyBase.Load TextBox1.Text = "4245.8" End Sub End Class
Bill -
Thursday, December 11, 2008 2:17 PMModerator
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- Marked As Answer by Martin Xie - MSFT Wednesday, December 17, 2008 10:43 AM
-
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 post. I 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 PMModerator
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- Marked As Answer by Martin Xie - MSFT Wednesday, December 17, 2008 10:46 AM
-
Thursday, December 11, 2008 6:49 PM
Cerberus Wang said: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.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?
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- Marked As Answer by Martin Xie - MSFT Wednesday, December 17, 2008 10:45 AM
-
Thursday, December 11, 2008 7:43 PM
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
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
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
Thanks JWolf011, I have tried, the formatnumber is a good work around for me.1 Dim S As Single = 1561.59999999999945 2 MessageBox.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

