2

Im doing a monthly payment project and I cant get the dollar amount to go to two decimals. I'm new to coding and dont know if theres something obvious I'm missing here.

Heres my code:

double monthlyP = (1 + monthlyRate);//P = Payment
double power = Math.pow(monthlyP, numMonths);
       monthlyP = power * monthlyRate;
double monthlyP2 = power - 1;
double FmonthlyP = (monthlyP/monthlyP2) * principle;//F = final
       FmonthlyP = (FmonthlyP * 100)/Math.round(100);

It keeps outputting 1319.0 insted .23. I've tried putting the Math.round in the front of the multiplication part and behind it and I cant get it to work.

Filburt
  • 16,221
  • 12
  • 59
  • 107
Austin
  • 35
  • 2

2 Answers2

2

Computers use 64 bits to represent a double. 64-bits, so that can hold exactly 2^64 different values.

Uhoh.

Because there are an infinite number of numbers, that's a big problem. It means that most (in fact, an infinite amount of them) numbers are not representable by double at all. So what does java do? It will simply silently round things to the nearest of the blessed numbers that it can represent.

The upshot of this is that using double for currency is a big mistake.

Don't do that.

The easy thing to do is to use a long, and represent the atomic unit for the monetary amount you're working with. cents for dollars and euros. satoshi's for bitcoin.

Your code then does all operations in terms of 'cents' (or satoshis or whatnot). When you 'render' your cents to the user, throw it through a formatting function. Now the whole 'how do I round' issue is moot.

Example formatting function:

public static String printEuro(long cents) {
   boolean negative = cents < 0;
   if (negative) cents = -cents;
   
   int wholes = cents / 100;
   int parts = cents % 100;
   return String.format("%s€%d.%02d", negative ? "-" : "", wholes, parts);
}

NB: longs are 64 bit too, but double needs to be able to represent, say, 1e300, or 0.000123, and long doesn't. If we're talking euros, a long can cover every amount of cents as long as the total euro amount has 18 digits or less. Even entire country GDPs per century doesn't get that high. If you really need to go that far, try BigInteger.

rzwitserloot
  • 44,252
  • 4
  • 27
  • 37
  • In general I would agree with you. But you still need to know how to round. Consider adding .075 sales tax to a meal. Sometimes the value would need to be rounded up whereas the fraction would be dropped with a long. – WJS Sep 30 '20 at 14:47
  • Each individual operation that ends up with fractions of cents needs its own rounding logic, and BigDecimal and friends isn't going to help you. It's, per situation, fairly obvious. For example, if you have a 4ct fee that needs to be paid by three accounts, to avoid abuse, you'd have to charge each account 2 cents, or, you randomly determine the person to pay 2ct, and you pay 1ct. "Well, with rounding you can solve all problems" - no you can't. Rounding is a distraction. – rzwitserloot Sep 30 '20 at 15:21
  • @WJS for 'ratio factor' math, such as, say, adding 0.075 sales tax, again you can't 'just' round. It depends on the scenario. For example, you're making the receipt and need to list the base price, then the tax, then the sum. How many receipts have you noticed a fractional sales tax on? None. So, you'd do: long tax = taxRounding(amount * 0.075);, long total = amount + tax;, with roundFair being whatever your laws indicate is appropriate. Probably (return (long) (amt + 0.5);. – rzwitserloot Sep 30 '20 at 15:24
0

Try using

DecimalFormat df = new DecimalFormat("#.##");

Full Documentation here

KBS_Shabib
  • 41
  • 1
  • 9