7

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?

Sophtware
  • 1,796
  • 2
  • 21
  • 34
  • Dealing with floating point errors: http://stackoverflow.com/questions/2248748/dealing-with-floating-point-errors-in-net – Greg Jul 08 '10 at 15:07
  • 3
    See this article: http://docs.sun.com/source/806-3568/ncg_goldberg.html TL;DR: don't use float or double for money calculations – Piskvor left the building Jul 08 '10 at 15:09
  • The value comes from a SQL Server database in the form of a Money value. The object wrapper class uses 'double' for these values, so I can't change the type of Amount. – Sophtware Jul 08 '10 at 15:11
  • 1
    You should use decimal instead of floating points whenever possible when dealing with currency. – diadem Jul 08 '10 at 15:12
  • Also, the server NEVER calculates any values from these Amount fields. They are just pass through, I think that's why the wrapper object uses 'double' for the type. – Sophtware Jul 08 '10 at 15:13
  • @Sophtware: But surely you could extract the value and typecast it to decimal? – Piskvor left the building Jul 08 '10 at 15:20
  • 4
    @Sophtware: you should raise a bug on the wrapper class because it is a bug to represent a money value as a double or float. – JeremyP Jul 08 '10 at 16:39

8 Answers8

21

You should really use decimal for money calculations.

((int)(580.55m * 100.0m)).ToString().Dump();
Jesper Palm
  • 7,170
  • 31
  • 36
8

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

Andrew Bezzub
  • 15,744
  • 7
  • 51
  • 73
4

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.

Donald Miner
  • 38,889
  • 8
  • 95
  • 118
  • 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 Jul 08 '10 at 15:15
  • Yes, that would probably work (I'm not familiar with C# and didn't know it had a round function built-in). – Donald Miner Jul 08 '10 at 15:16
  • you are guessing wildly. 58054.9999999... is no more representable accurately as a floating-point number than 580.55. – High Performance Mark Jul 08 '10 at 15:17
  • 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. – Donald Miner Jul 08 '10 at 15:27
3

Try

((int)(Math.Round(customerBatch.Amount * 100.0))).ToString()
ULysses
  • 978
  • 4
  • 9
0

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.

EDIT 2: As others have pointed out, it would be best to use some sort of fixed point decimal variable. This is better than my original solution because it would store the information about the location of the decimal point in the value where it belongs instead of in the code.

murgatroid99
  • 19,007
  • 10
  • 60
  • 95
0

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
  • 131,333
  • 52
  • 229
  • 284
0

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
  • 550
  • 6
  • 13
  • 2
    In other words, multiplying floating point numbers on a computer *does* introduce rounding errors. – Piskvor left the building Jul 08 '10 at 15:12
  • 1
    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 Jul 08 '10 at 16:16
  • 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. – sigfpe Jul 08 '10 at 16:36
0

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

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

jdot
  • 801
  • 1
  • 6
  • 9