Currency Math in VB.NET

  • Thread starter Thread starter Mitchell Vincent
  • Start date Start date
M

Mitchell Vincent

Just making sure I'm not missing the boat here, but are there any
special routines for doing currency math (fixed precision stuff) in .NET?

The wonderful problems of doing math on decimals tend to shine when
writing accounting software :-)

How are others dealing with this?
 
Just making sure I'm not missing the boat here, but are there any
special routines for doing currency math (fixed precision stuff) in .NET?

The wonderful problems of doing math on decimals tend to shine when
writing accounting software :-)

How are others dealing with this?

I think most work in double precision, truncate at 4 decimals, and transfer
the chopped off bits (accumulated) to their own account.


Seriously: I'm missing a currency type as well.

There's a workaround, but it induces a distinct feeling of going back in
time to COBOL.

Remember how you had to write an ADDONETOI subroutine to increment a
variable in COBOL? In .Net there's a currency datatype, but it knows no
operators + - / or *. No mixing of data types - you will NOT divide a
currency type by an integer, unless you yourself implement it as a
function.

Dim x1 As New SqlTypes.SqlMoney(0.0002)
Dim x2 As New SqlTypes.SqlMoney(0.0003)
Dim TWO As New SqlTypes.SqlMoney(2)
Dim x3 As SqlTypes.SqlMoney = SqlTypes.SqlMoney.Add(x1, x2)
Dim x4 As SqlTypes.SqlMoney = SqlTypes.SqlMoney.Divide(x3, TWO)
Debug.WriteLine(x4.ToString)

The result is 0.0003: standard rounding applied to .00025, whereas the
Math.Round function on a Double would, to many one's surprise, use banker's
rounding and say it's 0.0002. But that's topic for another discussion ;)
 
Lucvdv said:
Remember how you had to write an ADDONETOI subroutine to increment a
variable in COBOL? In .Net there's a currency datatype, but it knows no
operators + - / or *. No mixing of data types - you will NOT divide a
currency type by an integer, unless you yourself implement it as a
function.

Dim x1 As New SqlTypes.SqlMoney(0.0002)
Dim x2 As New SqlTypes.SqlMoney(0.0003)
Dim TWO As New SqlTypes.SqlMoney(2)
Dim x3 As SqlTypes.SqlMoney = SqlTypes.SqlMoney.Add(x1, x2)
Dim x4 As SqlTypes.SqlMoney = SqlTypes.SqlMoney.Divide(x3, TWO)
Debug.WriteLine(x4.ToString)

The result is 0.0003: standard rounding applied to .00025, whereas the
Math.Round function on a Double would, to many one's surprise, use banker's
rounding and say it's 0.0002. But that's topic for another discussion ;)


So you're using the SqlTypes.SqlMoney type for all your math operations?

It seems like the issue you just showed would be a reason not to!

I'm only in need of 2 precision, as I'm not dealing with anything
smaller than a cent.

Thanks!
 
So you're using the SqlTypes.SqlMoney type for all your math operations?

No, I got off lucky: I can use longs instead (no invoicing type of
applications, and all amounts I work with are multiples of 1 euro).
It seems like the issue you just showed would be a reason not to!

That depends. In my opinion it's Math.Round that's giving the wrong
result, not the other one.
I'm only in need of 2 precision, as I'm not dealing with anything
smaller than a cent.

The currency type that exists in VB6 for example is also fixed 4 digits.

Over here we even have a law that stipulates that all computations on
currency values must be performed with an internal precision of 4 decimals
(0.0001 euros), and only the final printed results should be rounded to two
decimals.

As long as only addition and subtraction are used it's the same, but
percentages is where it begins to matter, and in Europe there's no way to
get around those (tax: the VAT nuisance).

It's perfectly possible here to have an invoice where the printed numbers
don't add up, if VAT amounts are listed per individual item (which is not a
requirement).
 
Mitchell,
I normally do all my "currency math" with System.Decimal, of course when I
use multiplication or division I need to be certain to round the numbers
"correctly".

In VB 2005 you can use Operator Overloading to define your own "special
routines" that handle any specific rounding issues, thus encapsulating any
possible rounding concerns. VB 2005's Operator Overloading also allows you
to leverage the overloaded operators on SqlMoney.

Hope this helps
Jay

| Just making sure I'm not missing the boat here, but are there any
| special routines for doing currency math (fixed precision stuff) in .NET?
|
| The wonderful problems of doing math on decimals tend to shine when
| writing accounting software :-)
|
| How are others dealing with this?
|
| --
| - Mitchell Vincent
| - kBilling - Invoices Made Easy!
| - http://www.k-billing.com
 
Jay said:
Mitchell,
I normally do all my "currency math" with System.Decimal, of course when I
use multiplication or division I need to be certain to round the numbers
"correctly".

How do you handle it exactly?

I've always stored my money values as ints instead of fooling with the
decimal game but now that is more of a problem than a help.

I like the overloading idea - do you have any examples that I might go by?

Thanks a million!
 
Mitchell,
I use Math.Round & Decimal.Round most of the time.

..NET 2.0 (VS.NET 2005, due out late in 2005) has overloaded Math.Round to
account for other rounding methods (mid point up or even/bankers).

http://msdn2.microsoft.com/library/ms131274(en-us,vs.80).aspx


As suggested in the following previous post of mine, I find banker's
rounding (the current Math.Round function) to be the "logical" choice.

---x--- cut here ---x---
The way I understand it is:

Rather then always rounding the mid point up, "half of the time" you are
rounding it up & "half of the time" you are rounding it down.

The two "half of the time" together will average themselves out of the
picture.

When you always rounding the mid point up, then you start accumulating all
the fractions, rather then average them out...

The following demonstrates this:

Public Shared Sub Main()
Dim value1 As Decimal = 1.235D
Dim value2 As Decimal = 1.245D

Dim value3 As Decimal = value1 + value2

Dim value4 As Decimal = Decimal.Round(value1, 2) +
Decimal.Round(value2, 2)
Dim value5 As Decimal = RoundUp(value1, 2) + RoundUp(value2, 2)

Debug.WriteLine(value1, "value1")
Debug.WriteLine(value2, "value2")
Debug.WriteLine(value3, "value3")
Debug.WriteLine(value4, "value4")
Debug.WriteLine(value5, "value5")
End Sub

' may not handle negative value correctly...
Private Shared Function RoundUp(ByVal value As Decimal, ByVal decimals
As Integer) As Decimal
decimals = CInt(10 ^ decimals)
value *= decimals
value = Decimal.Truncate(value + 0.5D)
value /= decimals
Return value
End Function

Notice how value4 & value5 are off by 1, if you are lot of rounding, this
can add up significantly. When you use banking rounding, this difference
will not add up as quickly or as much...


---x--- cut here ---x---

| I like the overloading idea - do you have any examples that I might go by?
NOTE: You need C# 2002 or higher or VB 2005 or higher:

http://msdn.microsoft.com/library/d...y/en-us/dnvs05/html/vboperatoroverloading.asp

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnhcvs03/html/vs03h12.asp

http://www.panopticoncentral.net/archive/2004/07/01/1338.aspx

Hope this helps
Jay

| Jay B. Harlow [MVP - Outlook] wrote:
| > Mitchell,
| > I normally do all my "currency math" with System.Decimal, of course when
I
| > use multiplication or division I need to be certain to round the numbers
| > "correctly".
|
| How do you handle it exactly?
|
| I've always stored my money values as ints instead of fooling with the
| decimal game but now that is more of a problem than a help.
|
| I like the overloading idea - do you have any examples that I might go by?
|
| Thanks a million!
|
| --
| - Mitchell Vincent
| - kBilling - Invoices Made Easy!
| - http://www.k-billing.com
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top