1

I have a input to a method that represents an amount of money, a total price of X items.
This can be an amount under a number of currencies and is represented by a double.
Question: I want to break this amount into the price for each of the X items. Considering that the amount itself is a double my concern is that if I simply do: amount/X I could get a number that number*X does not give me the amount exactly due to e.g. rounding. How can I do this correctly?
Note: Please give me help taking for granted that I can not change the amount to be something other than a double

Jim
  • 18,826
  • 34
  • 135
  • 254
  • You can convert the double to a `BigDecimal` and do the calculations with that. – Kayaman Aug 25 '15 at 05:49
  • @Kayaman: How do you think that will help? – tmyklebu Aug 25 '15 at 13:53
  • @tmyklebu Well it'll allow you to do precise calculations. However if other parts expect a `double`, it just means that the code was broken to begin with. If you're dealing with money, you need to work with `BigDecimal` until you pretty print the result for a user (who most likely doesn't want to see values such as `10.000000000256`). – Kayaman Aug 25 '15 at 14:14
  • @Kayaman: BigDecimal can't represent rationals exactly. It's still a floating-point format, and the pigeonhole principle still applies---there is still a BigDecimal f such that (1/f)*f != 1. ("If you're dealing with money, you need to work with BigDecimal" is totally false, by the way; plenty of financial applications get by just fine using doubles.) – tmyklebu Aug 25 '15 at 14:31

4 Answers4

1

As you already mentioned, this is not possible in every case. Usually, when the total price is really the product of the single item price and a quantity, the resulting double will have enough precision to do this calculation. But generally you have to store both prices.

I implemented a whole ERP, and we also have a feature where the user can specify the total line amount directly, maybe to round the sum after discussing with a customer. And this makes it possible to sell 7 items for 1000$. Then there is no way to create a precise representation of a price for a single item. And therefore we also store the item price rounded (cut after 4 digits).

To come back to your answer: Depending on context you will need the total and single item amount later anyway, so just store both.

Daniel
  • 27,718
  • 20
  • 89
  • 133
  • I don't have an issue keeping both. But showing a price in 4 digits (because only this way we can multiply to get back to the correct amount), was that an issue to your customer? I don't remember ever seeing prices with 4 digits after decimal – Jim Aug 25 '15 at 06:21
  • At least in my country prices for fuel are given in higher precision that useful. A litre costs e.g. 1,299EUR, and are rounded after multiplying with the amount of fuel loaded. This is obviously a scam, because 1,30 is the effective price. Nonetheless, a single item price can always have higher precision that the line item amount. – Daniel Aug 27 '15 at 09:25
0

You can use BigDecimal class:

    double a=456.6556756;
    BigDecimal amount=BigDecimal.valueOf(a);
    BigDecimal priceOfEachItem=amount.divide(BigDecimal.valueOf(8));   
    BigDecimal amountAgain=priceOfEachItem.multiply(BigDecimal.valueOf(8));
    System.out.println(amountAgain.doubleValue());

Output:456.6556756

As you can see you can get the exact original amount

hermit
  • 1,048
  • 1
  • 6
  • 16
  • Yes but what will happen if I convert the result back to double to pass to other functions? – Jim Aug 25 '15 at 06:22
  • @Jim You will have no problem at all. You can retrive the double value as: double value=amountAgain.doubleValue(); and pass it to other functions – hermit Aug 25 '15 at 06:26
  • But if you want a complete precision accuracy then you have to use BigDecimal in other functions too.. – hermit Aug 25 '15 at 06:28
  • This division will only work for an exact multiple. What if the per unit price is not a cent. BTW you can't have `$456.6556756` – Peter Lawrey Aug 25 '15 at 07:23
  • The question asks for exact multiple as far as I can understand. :) I only wanted to show that BigDecimal preserves the precision with complete accuracy. Did not think about the currency aspect :) – hermit Aug 25 '15 at 07:28
-1

You can do this by taking the single item price as double value which won't round off the value(Yes,you may limit the number of digits after decimal).

double totalPrice = 500.33;
int totalItems = 6;
double singleItemPrice = totalPrice/totalItems;
System.out.println("Single Item Price = " + singleItemPrice);
System.out.println("Total Price = " + (singleItemPrice * totalItems));
DecimalFormat df = new DecimalFormat("#.##"); 
Double formatted = Double.parseDouble(df.format(singleItemPrice * totalItems)); 
System.out.println("Formatted Value = "formatted);

Output:

Single Item Price = 83.38833333333334
Total Price = 500.33000000000004
Formatted Value = 500.33

Or use BigDecimal as mentioned by someone else(but maybe the above will have more precision, let us know about that):

import java.math.BigDecimal;

BigDecimal premium = BigDecimal.valueOf(1586.6d);
BigDecimal netToCompany = BigDecimal.valueOf(708.75d);
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

Output:

877.85 = 1586.6 - 708.75

Hope this was what you were looking for.

Tom
  • 16,842
  • 17
  • 45
  • 54
Droy
  • 173
  • 2
  • 17
  • Yes but what will happen if I convert the result back to double to pass to other functions? – Jim Aug 25 '15 at 06:23
-1

You can use Math.round for rounding. The problem you have is that you don't know what rounding for you amount/X you need. This is a problem whether you use double or BigDecimal

You can do this

public static double round2(double d) {
    if (d < -Long.MAX_VALUE / 100 || d > Long.MAX_VALUE / 100)
        return d;
    return Math.round(d * 100) / 100.0;
}

public static void main(String[] args) throws IOException {
    double amount = 1000.00;
    int units = 9;
    System.out.println("amount: " + amount );
    System.out.println("units: " + units);
    double perUnit = amount / units;
    double amount2 = round2(perUnit * units);
    System.out.println("perUnit: " + perUnit);
    System.out.println("amount2: " + amount2);
}

prints

amount: 1000.0
units: 9
perUnit: 111.11111111111111
amount2: 1000.0
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130