{"id":"object-methods-money","slug":"money-and-rounding","title":"Money and rounding errors","text":"### Doubles aren't exact\n\nTry adding a few small decimal numbers:\n\n```java\nSystem.out.println(0.1 + 0.2);\ndouble total = 0.0;\nfor (int i = 0; i < 10; i++) {\n    total = total + 0.1;\n}\nSystem.out.println(total);\nSystem.out.println(total == 1.0);\n```\n\n```output\n0.30000000000000004\n0.9999999999999999\nfalse\n```\n\nA `double` stores its number in binary, with a fixed number of binary digits. Most decimal fractions, 0.1 among them, can't be written exactly that way, just as 1/3 can't be written exactly as a decimal number (0.3333...). So a double holds the closest number it can, which is off by a tiny amount. Usually the difference is too small to notice, but it can show after a single sum, as in `0.1 + 0.2`, and the errors add up as more values are summed, as in the loop. And `==` can say `false` for two doubles that should be equal.\n\nFor a temperature or a distance, errors that small don't matter. For money they do: a balance should never be a fraction of a cent off.\n\n### Money in whole cents\n\nThe simple fix is to count money in its smallest unit, cents, and to keep it in an `int`: 2.50 euros is 250 cents. Adding and subtracting whole numbers is exact, as long as the result fits in an `int`: its largest value is 2147483647, so up to about 21 million euros in cents. (Java also has a class for exact decimal numbers, `BigDecimal`, but whole cents in an `int` are all this course needs.)\n\nTo show cents as euros, divide by 100 for the euros, and take what's left with `%` for the cents. The cents always need two digits: 205 cents is `2.05`, not `2.5`, so cents below 10 get a `0` in front. This print quota for a library printer keeps its balance that way:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        PrintQuota quota = new PrintQuota(500);\n        System.out.println(quota);\n        quota.printPages(7);\n        System.out.println(quota);\n        boolean printed = quota.printPages(30);\n        System.out.println(printed);\n        System.out.println(quota);\n    }\n}\n// ==== PrintQuota.java ====\npublic class PrintQuota {\n    private int cents;\n\n    public PrintQuota(int cents) {\n        this.cents = cents;\n    }\n\n    public boolean printPages(int pages) {\n        int cost = pages * 15;\n        if (cost > this.cents) {\n            return false;\n        }\n        this.cents = this.cents - cost;\n        return true;\n    }\n\n    public String toString() {\n        int euros = this.cents / 100;\n        int rest = this.cents % 100;\n        if (rest < 10) {\n            return euros + \".0\" + rest + \" euros\";\n        }\n        return euros + \".\" + rest + \" euros\";\n    }\n}\n```\n\n```output\n5.00 euros\n3.95 euros\nfalse\n3.95 euros\n```\n\n- The balance is in cents, in an `int`. A page costs 15 cents, so 7 pages cost exactly 105 cents.\n- `printPages` has a rule: it prints only when the balance covers the cost, so the balance never goes below zero. It returns `true` or `false`, so the caller knows whether it worked. 30 pages would cost 450 cents, more than the 395 left.\n- Only `toString` shows the money as euros. Every calculation stays in whole cents.\n","task":"A `CoinJar` keeps its money in cents. Add a `toString` that shows the money as euros and cents, with two digits for the cents and the word `euros` after them. The check adds a 2-euro coin (200 cents) and a 5-cent coin, and prints the jar:\n\n```\n2.05 euros\n```\n","kind":"code","seed":"// ==== CoinJar.java ====\npublic class CoinJar {\n    private int cents;\n\n    public CoinJar() {\n        this.cents = 0;\n    }\n\n    public void addCoin(int cents) {\n        this.cents = this.cents + cents;\n    }\n\n    // Write the toString method here.\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        CoinJar jar = new CoinJar();\n        jar.addCoin(200);\n        jar.addCoin(5);\n        System.out.println(jar);\n    }\n}\n","solution":"// ==== CoinJar.java ====\npublic class CoinJar {\n    private int cents;\n\n    public CoinJar() {\n        this.cents = 0;\n    }\n\n    public void addCoin(int cents) {\n        this.cents = this.cents + cents;\n    }\n\n    public String toString() {\n        int euros = this.cents / 100;\n        int rest = this.cents % 100;\n        if (rest < 10) {\n            return euros + \".0\" + rest + \" euros\";\n        }\n        return euros + \".\" + rest + \" euros\";\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        CoinJar jar = new CoinJar();\n        jar.addCoin(200);\n        jar.addCoin(5);\n        System.out.println(jar);\n    }\n}\n","hints":["`this.cents / 100` gives the whole euros, and `this.cents % 100` the cents left over.","When the cents are below 10, put a `0` in front of them: `if (rest < 10) { return euros + \".0\" + rest + \" euros\"; }`"],"tests":[{"name":"Test 1","stdin":"","call":"CoinJar jar = new CoinJar();\njar.addCoin(200);\njar.addCoin(5);\nSystem.out.println(jar);\n","expect":"2.05 euros","hidden":false},{"name":"Test 2","stdin":"","call":"CoinJar jar = new CoinJar();\nSystem.out.println(jar);\n","expect":"0.00 euros","hidden":true},{"name":"Test 3","stdin":"","call":"CoinJar jar = new CoinJar();\njar.addCoin(50);\njar.addCoin(20);\njar.addCoin(20);\nSystem.out.println(jar);\njar.addCoin(1144);\nSystem.out.println(jar);\n","expect":"0.90 euros\n12.34 euros","hidden":true},{"name":"Test 4","stdin":"","call":"CoinJar jar = new CoinJar();\njar.addCoin(100);\nSystem.out.println(\"In the jar: \" + jar);\n","expect":"In the jar: 1.00 euros","hidden":true}],"require":[],"forbid":[{"pattern":"\\b(double|float)\\b","message":"Keep the money in whole cents: no `double` is needed."}],"more":[{"task":"A parking meter takes only coins of 10, 20, 50, 100 and 200 cents, and every 10 cents buys 3 minutes. Write `insert(int coin)`, which adds a valid coin to the money paid and returns `true`, but returns `false` for any other value and leaves the money as it was. Also write `minutes()`, which returns the minutes the money paid buys. `toString` is ready, and it calls `minutes()`. The check inserts 100, 5 and 20 cents, printing what `insert` returns each time, and then prints the meter:\n\n```\ntrue\nfalse\ntrue\n120 cents, 36 minutes\n```\n","kind":"code","seed":"// ==== ParkingMeter.java ====\npublic class ParkingMeter {\n    private int paid;\n\n    public ParkingMeter() {\n        this.paid = 0;\n    }\n\n    // Write insert and minutes here.\n\n    public String toString() {\n        return this.paid + \" cents, \" + this.minutes() + \" minutes\";\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ParkingMeter meter = new ParkingMeter();\n        meter.insert(200);\n        if (!meter.insert(2)) {\n            System.out.println(\"Coin returned\");\n        }\n        System.out.println(meter);\n    }\n}\n","solution":"// ==== ParkingMeter.java ====\npublic class ParkingMeter {\n    private int paid;\n\n    public ParkingMeter() {\n        this.paid = 0;\n    }\n\n    public boolean insert(int coin) {\n        if (coin == 10 || coin == 20 || coin == 50 || coin == 100 || coin == 200) {\n            this.paid = this.paid + coin;\n            return true;\n        }\n        return false;\n    }\n\n    public int minutes() {\n        return this.paid / 10 * 3;\n    }\n\n    public String toString() {\n        return this.paid + \" cents, \" + this.minutes() + \" minutes\";\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ParkingMeter meter = new ParkingMeter();\n        meter.insert(200);\n        if (!meter.insert(2)) {\n            System.out.println(\"Coin returned\");\n        }\n        System.out.println(meter);\n    }\n}\n","hints":["`insert` returns a boolean. Check the coin with `||`: `if (coin == 10 || coin == 20 || ...) { ... return true; }`, and `return false;` after the `if`.","Every coin is a whole number of 10 cents, so `minutes()` can be `return this.paid / 10 * 3;`"],"tests":[{"name":"Test 1","stdin":"","call":"ParkingMeter meter = new ParkingMeter();\nSystem.out.println(meter.insert(100));\nSystem.out.println(meter.insert(5));\nSystem.out.println(meter.insert(20));\nSystem.out.println(meter);\n","expect":"true\nfalse\ntrue\n120 cents, 36 minutes","hidden":false},{"name":"Test 2","stdin":"","call":"ParkingMeter meter = new ParkingMeter();\nSystem.out.println(meter);\nmeter.insert(200);\nmeter.insert(50);\nmeter.insert(10);\nSystem.out.println(meter);\n","expect":"0 cents, 0 minutes\n260 cents, 78 minutes","hidden":true},{"name":"Test 3","stdin":"","call":"ParkingMeter meter = new ParkingMeter();\nSystem.out.println(meter.insert(30));\nSystem.out.println(meter.insert(500));\nSystem.out.println(meter.insert(0));\nSystem.out.println(meter.insert(-10));\nSystem.out.println(meter);\n","expect":"false\nfalse\nfalse\nfalse\n0 cents, 0 minutes","hidden":true},{"name":"Test 4","stdin":"","call":"ParkingMeter meter = new ParkingMeter();\nmeter.insert(50);\nmeter.insert(50);\nSystem.out.println(meter.minutes());\n","expect":"30","hidden":true}],"require":[],"forbid":[{"pattern":"\\b(double|float)\\b","message":"Keep the money in whole cents: no `double` is needed."}]},{"task":"A `VendingMachine` keeps the money put in as credit, in cents. Write two methods. `buy(int price)` takes the price off the credit and returns `true` when the credit covers the price; otherwise it returns `false`, and the credit stays as it was. `change()` returns the credit that's left and sets the credit to 0, as when the machine gives the coins back. The check puts in 200 cents, tries to buy two things that cost 150 cents, and asks for change twice:\n\n```\ntrue\nfalse\n50\n0\n```\n","kind":"code","seed":"// ==== VendingMachine.java ====\npublic class VendingMachine {\n    private int credit;\n\n    public VendingMachine() {\n        this.credit = 0;\n    }\n\n    public void insert(int cents) {\n        this.credit = this.credit + cents;\n    }\n\n    // Write buy and change here.\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        VendingMachine machine = new VendingMachine();\n        machine.insert(200);\n        if (machine.buy(150)) {\n            System.out.println(\"Enjoy your snack!\");\n        }\n        System.out.println(\"Your change: \" + machine.change() + \" cents\");\n    }\n}\n","solution":"// ==== VendingMachine.java ====\npublic class VendingMachine {\n    private int credit;\n\n    public VendingMachine() {\n        this.credit = 0;\n    }\n\n    public void insert(int cents) {\n        this.credit = this.credit + cents;\n    }\n\n    public boolean buy(int price) {\n        if (this.credit >= price) {\n            this.credit = this.credit - price;\n            return true;\n        }\n        return false;\n    }\n\n    public int change() {\n        int left = this.credit;\n        this.credit = 0;\n        return left;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        VendingMachine machine = new VendingMachine();\n        machine.insert(200);\n        if (machine.buy(150)) {\n            System.out.println(\"Enjoy your snack!\");\n        }\n        System.out.println(\"Your change: \" + machine.change() + \" cents\");\n    }\n}\n","hints":["`buy` returns a boolean: `if (this.credit >= price) { ... return true; }`, and `return false;` after it.","`change()` has to remember the credit before setting it to 0: `int left = this.credit;`, then `this.credit = 0;`, then `return left;`"],"tests":[{"name":"Test 1","stdin":"","call":"VendingMachine machine = new VendingMachine();\nmachine.insert(200);\nSystem.out.println(machine.buy(150));\nSystem.out.println(machine.buy(150));\nSystem.out.println(machine.change());\nSystem.out.println(machine.change());\n","expect":"true\nfalse\n50\n0","hidden":false},{"name":"Test 2","stdin":"","call":"VendingMachine machine = new VendingMachine();\nmachine.insert(100);\nSystem.out.println(machine.buy(100));\nSystem.out.println(machine.change());\n","expect":"true\n0","hidden":true},{"name":"Test 3","stdin":"","call":"VendingMachine machine = new VendingMachine();\nmachine.insert(50);\nmachine.insert(20);\nSystem.out.println(machine.buy(80));\nmachine.insert(10);\nSystem.out.println(machine.buy(80));\nSystem.out.println(machine.change());\n","expect":"false\ntrue\n0","hidden":true},{"name":"Test 4","stdin":"","call":"VendingMachine machine = new VendingMachine();\nSystem.out.println(machine.buy(1));\nmachine.insert(90);\nSystem.out.println(machine.change() + machine.change());\n","expect":"false\n90","hidden":true}],"require":[],"forbid":[{"pattern":"\\b(double|float)\\b","message":"Keep the money in whole cents: no `double` is needed."}]}]}