2. • Surely everyone orders and pays the same way as us?
• In mainland Europe it is common to pay for goods by bank
transfer
• Brazil charges 60% import tax
• In Japan it is common to have goods delivered and to pay for
them later
Worldwide
3. • 3D Secure
• An unpopular inconvenience
• Unusual tax regimes
• US tax calculation is so complex it requires a 3rd
party service
• Not charging the right amount
• This is going to upset most people
Problems with payment
4. • Promotions
• Pricing
• Tax
• So how do we make sure that you are paying the right
amount?
Complicating the problem
5. • Use a double?
•
Memory
efficient
•
High
precision
•
Fast
CPU
backed
calcula<ons
•
Secret
rounding
•
But...
rounding
problems,
but
would
we
really
see
those
in
real
life?
Modelling money
6. • JVM primitives often have lots of constants to make it easier
for us and prevent the use of magic numbers, like MIN_VALUE.
double adjustment = -5.0D;double threshold =
10.0D;if(adjustment>=Double.MIN_VALUE &&
adjustment<=threshold) { System.out.println("Small
adjustment");} else { System.out.println("Big
adjustment");}
Output:
Big adjustment
Useful constants
7. • Double.MIN_VALUE is not really the minimum value
possible.
• It is “A constant holding the smallest positive nonzero value
of type double, 2-1074”
Urr, why?
8. • What does this do?
• double amount = 100.05;
• double discount = amount * 0.10;
• double total = amount - discount;
• NumberFormat money = NumberFormat.getCurrencyInstance();
• System.out.println("Subtotal="+ money.format(amount));
• System.out.println("Discount=" + money.format(discount));
• System.out.println("Total=" + money.format(total));
Subtotal=£100.05Discount=£10.00Total=£90.04
Trouble with double
9. • Subtotal=£100.05
• Discount=£10.00
• Total=£90.04
• But what were the real values hidden from us?
• Subtotal=100.049999999999997157829056959599256515502
• Discount=10.005000000000000781597009336110204458236694
• Total=90.045000000000001705302565824240446090698
• Double has helpfully rounded down for us. The rounding behaviour of
NumberFormat can be controlled by RoundingMode in JDK6+.
Explain that
10. • But that was a multiplication, surely that can’t happen to a
simple addition?
• boolean x = (0.7d+0.1d) == (0.9d-0.1d);
• System.out.println(x);
false
0.7d+0.1d == 0.7999999999999999
0.9d-0.1d == 0.8
Double trouble
11. • What could this really mean, it’s just a penny.
Tiny amounts of money
Woman eaten by a computer
Gorman’s salami slicing Blackmail
Ersatz Kryptonite Bad superman
Weather control satellite
12. • It’s obviously not right and very salient
• It undermines the end customer’s confidence
• Your client thinks you can’t even do basic arithmetic right -
“get me a calculator, I’ll show them how to add up”
So why is this really bad?
13. • So why don’t we just use a long instead, like all the
banks do?
• System needs to be engineered from the ground up to use
this approach
• Can’t easily carry fractional units through calculation
• End users/administrators don’t understan the approach when
looking at logs and managing prices
The long way around
14. • Infinite precision
• Internal string representation
• Slow, but is this really going to affect your system?
Enter big decimal
15. Here’s a simple example:
• BigDecimal amount = new BigDecimal(“10”);
• BigDecimal discount = new BigDecimal(“2”);
• amount.subtract(discount);
• System.out.println(amount);
Output:
10
BigDecimals are immutable, so the new amount is calculated and
immediately discarded.
Simple API...
17. • You may have legacy code using doubles
• How do we interface with it?
double discount = 0.1D; // Call your legacy system here
System.out.println(new BigDecimal(discount));
Output: 0.1000000000000000055511151231257827021181583404541015625
System.out.println(BigDecimal.valueOf(discount));
Output:
0.1
Bridging the gap