tags:

views:

205

answers:

8

This is what I am doing, which works 99.999% of the time:

((int)(customerBatch.Amount * 100.0)).ToString()

The Amount value is a double. I am trying to write the value out in pennies to a text file for transport to a server for processing. The Amount is never more than 2 digits of precision.

If you use 580.55 for the Amount, this line of code returns 58054 as the string value.

This code runs on a web server in 64-bit.

Any ideas?

+18  A: 

You should really use decimal for money calculations.

((int)(580.55m * 100.0m)).ToString().Dump();
Jesper Palm
+1, the docs even recommend using decimal for financial / monetary calculations: http://msdn.microsoft.com/en-us/library/364x0z75.aspx
Justin Ethier
Jesper Palm
So you're suggesting using: ((int)((decimal)customerBatch.Amount * 100.0m)).ToString()
Sophtware
@Sophtware You can remove the cast to int
Jesper Palm
I will give that a try and let you know how it works out. Thanks!
Sophtware
This works:Math.Truncate((decimal)580.55 * 100.0m).ToString();
Sophtware
It might work for that value, but there will be others that don't. Once your 'wrapper' converts SQL MONEY to double the damage is done. You gotta get rid of the doubles.
n8wrl
+7  A: 

You could use decimal values for accurate calculations. Double is floating point number which is not guaranteed to be precise during calculations.

Andrew Bezzub
A: 

My suggestion would be to store the value as the integer number of pennies and take dollars_part = pennies / 100 and cents_part = pennies % 100. This will completely avoid rounding errors. Edit: when I wrote this post, I did not see that you could not change the number format. The best answer is probably using the round method as others have suggested.

murgatroid99
+3  A: 

I'm guessing that 580.55 is getting converted to 58054.99999999999999999999999999..., in which case int will round it down to 58054. You may want to write your own function that converts your amount to a int with some sort of rounding or threshold to make this not happen.

orangeoctopus
If that's the case, int is taking the integer part, rather than rounding. How about something like ((int)(round(customerBatch.Amount * 100.0))).ToString() then?
Alejandro
Yes, that would probably work (I'm not familiar with C# and didn't know it had a round function built-in).
orangeoctopus
@orangeoctopus: you are guessing wildly. 58054.9999999... is no more representable accurately as a floating-point number than 580.55.
High Performance Mark
My point is the number 580.55 is probably not being stored as 580.55. It may be represented as 580.55 when it is printed out. If it is being stored as a number LESS THAN 580.55 (regardless as what it is printed as), it may get rounded down by conversion to an int.
orangeoctopus
A: 

You really should not be using a double value to represent currency, due to rounding errors such as this.

Instead you might consider using integral values to represent monetary amounts, so that they are represented exactly. To represent decimals you can use a similar trick of storing 580.55 as the value 58055.

Justin Ethier
A: 

no, multiplying does not introduce rounding errors but not all values can by represented by floating point numbers. x.55 is one of them )

yatagarasu
In other words, multiplying floating point numbers on a computer *does* introduce rounding errors.
Piskvor
The first rounding error occurred when 580.55 was stored in a 'double' as 580.5499999999xxx. Multiplying it by 100 yielded 58054.99999999xxx, which is not really any less accurate than 580.5499999999xxx.
supercat
Of course multiplication of floating point types introduces rounding errors. Multiplying two N bit mantissas requires about 2N bits to store the product exactly. You have to lose about N bits to store the result back in the same type again.
A: 

Decimal has more precision than a double. Give decimal a try.

http://msdn.microsoft.com/en-us/library/364x0z75%28VS.80%29.aspx

jdot
+3  A: 

Try

((int)(Math.Round(customerBatch.Amount * 100.0))).ToString()
ULysses