payments nick
DESCRIPTION
TRANSCRIPT
Payment
Nick Vincent neoworks
• 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
• 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
• Promotions • Pricing • Tax • So how do we make sure that you are paying the right amount?
Complicating the problem
• 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
• 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
• 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?
• 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
• 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
• 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
• 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
• 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?
• 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
• Infinite precision • Internal string representation • Slow, but is this really going to affect your system?
Enter big decimal
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...
A matter of scale
• 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