Currency

From Rosetta Code
Currency is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Task

Show how to represent currency in a simple example, using a data type that represent exact values of dollars and cents.


Note

The IEEE 754 binary floating point representations of numbers like   2.86   and   .0765   are not exact.

For this example, data will be two items with prices in dollars and cents, a quantity for each, and a tax rate.

Use the values:

  • 4000000000000000 hamburgers at $5.50 each
  • 2 milkshakes at $2.86 each, and
  • a tax rate of 7.65%.


(That number of hamburgers is a 4 with 15 zeros after it.   The number is contrived to exclude naïve task solutions using 64 bit floating point types.)

Compute and output (show results on this page):

  • the total price before tax
  • the tax
  • the total with tax


The tax value must be computed by rounding to the nearest whole cent and this exact value must be added to the total price before tax.

The output must show dollars and cents with a decimal point.

The three results displayed should be:

  • 22000000000000005.72
  • 1683000000000000.44
  • 23683000000000006.16


Dollar signs and thousands separators are optional.

AWK[edit]

version 1[edit]

 
# syntax: GAWK -M -f CURRENCY.AWK
# using GNU Awk 4.1.1, API: 1.1 (GNU MPFR 3.1.2, GNU MP 5.1.2)
BEGIN {
PREC = 100
hamburger_p = 5.50
hamburger_q = 4000000000000000
hamburger_v = hamburger_p * hamburger_q
milkshake_p = 2.86
milkshake_q = 2
milkshake_v = milkshake_p * milkshake_q
subtotal = hamburger_v + milkshake_v
tax = subtotal * .0765
printf("%-9s %8s %18s %22s\n","item","price","quantity","value")
printf("hamburger %8.2f %18d %22.2f\n",hamburger_p,hamburger_q,hamburger_v)
printf("milkshake %8.2f %18d %22.2f\n\n",milkshake_p,milkshake_q,milkshake_v)
printf("%37s %22.2f\n","subtotal",subtotal)
printf("%37s %22.2f\n","tax",tax)
printf("%37s %22.2f\n","total",subtotal+tax)
exit(0)
}
 

Output:

item         price           quantity                  value
hamburger     5.50   4000000000000000   22000000000000000.00
milkshake     2.86                  2                   5.72

                             subtotal   22000000000000005.72
                                  tax    1683000000000000.41
                                total   23683000000000006.13

version 2[edit]

 
# syntax: GAWK -M -f CURRENCY2.AWK
# using GNU Awk 4.1.1, API: 1.1 (GNU MPFR 3.1.2, GNU MP 5.1.2)
# INT is used to define values and do math; results then converted to FLOAT
BEGIN {
PREC = 100
hamburger_p = 550
hamburger_q = 4000000000000000
hamburger_v = hamburger_p * hamburger_q
milkshake_p = 286
milkshake_q = 2
milkshake_v = milkshake_p * milkshake_q
subtotal = hamburger_v + milkshake_v
tax = subtotal * 765
subtotal /= 100
tax /= 1000000
printf("%-9s %8s %18s %22s\n","item","price","quantity","value")
printf("hamburger %8.2f %18d %22.2f\n",hamburger_p/100,hamburger_q,hamburger_v/100)
printf("milkshake %8.2f %18d %22.2f\n\n",milkshake_p/100,milkshake_q,milkshake_v/100)
printf("%37s %22.2f\n","subtotal",subtotal)
printf("%37s %22.2f\n","tax",tax)
printf("%37s %22.2f\n","total",subtotal+tax)
exit(0)
}
 

Output:

item         price           quantity                  value
hamburger     5.50   4000000000000000   22000000000000000.00
milkshake     2.86                  2                   5.72

                             subtotal   22000000000000005.72
                                  tax    1683000000000000.44
                                total   23683000000000006.16

C[edit]

This implementation uses the GMP library for arbitrary precision arithmetic. The only data type used here is mpf_t, the following text is from the GMP documentation :

Floating point number or Float for short, is an arbitrary precision mantissa with a limited precision exponent. The C data type for such objects is mpf_t. For example:

mpf_t fp;

One remark about the code, notice that for setting all other variables the mpf_set_d function is used:

 
mpf_set_d(burgerUnitPrice,5.50);
mpf_set_d(milkshakePrice,2 * 2.86);
mpf_set_d(burgerNum,4000000000000000);
mpf_set_d(milkshakeNum,2);
 

But when it comes to the tax rate, it's mpf_set_str:

 
mpf_set_str(tax,"0.0765",10);
 

The reason is a weird rounding off error which happens if the mpf_set_d function is used. Documentation and example usages of GMP are very rare on the net possibly because it is used almost exclusively by academia and high tech industries. The implementation below is the result of a lot of fiddling, gotchas and lessons learnt, just how good programming should always be :)

Library: GMP
 
/*Abhishek Ghosh, 8th November 2017*/
 
#include<stdio.h>
#include<gmp.h>
 
int main()
{
mpf_t burgerUnitPrice, milkshakePrice, burgerTotalPrice, totalPrice, tax, burgerNum, milkshakeNum;
 
mpf_inits(burgerUnitPrice, milkshakePrice, burgerTotalPrice, totalPrice, tax,burgerNum, milkshakeNum,NULL);
 
mpf_set_d(burgerUnitPrice,5.50);
mpf_set_d(milkshakePrice,2 * 2.86);
mpf_set_d(burgerNum,4000000000000000);
mpf_set_d(milkshakeNum,2);
 
mpf_mul(burgerTotalPrice,burgerNum,burgerUnitPrice);
mpf_add(totalPrice,burgerTotalPrice,milkshakePrice);
 
mpf_set_str(tax,"0.0765",10);
mpf_mul(tax,totalPrice,tax);
 
gmp_printf("\nTotal price before tax : $ %.*Ff", 2, totalPrice);
gmp_printf("\nTotal tax : $ %.*Ff", 2, tax);
 
mpf_add(totalPrice,totalPrice,tax);
 
gmp_printf("\nTotal price after tax : $ %.*Ff", 2, totalPrice);
 
return 0;
}
 

Output:

Total price before tax : $ 22000000000000005.72
Total tax : $ 1683000000000000.44
Total price after tax : $ 23683000000000006.16

C#[edit]

The built in C# type decimal has a max value of 79228162514264337593543950335.

using System;
using System.Collections.Generic;
 
namespace Currency
{
class Program
{
static void Main(string[] args)
{
MenuItem hamburger = new MenuItem() { Name = "Hamburger", Price = 5.5M };
MenuItem milkshake = new MenuItem() { Name = "Milkshake", Price = 2.86M };
 
IList<CartItem> cart = new List<CartItem>();
cart.Add(new CartItem() { item = hamburger, quantity = 4000000000000000 });
cart.Add(new CartItem() { item = milkshake, quantity = 2 });
 
decimal total = CalculateTotal(cart);
 
Console.WriteLine(string.Format("Total before tax: {0:C}", total));
 
// Add Tax
decimal tax = total * 0.0765M;
 
Console.WriteLine(string.Format("Tax: {0:C}", tax));
 
total += tax;
 
Console.WriteLine(string.Format("Total with tax: {0:C}", total));
}
 
private static decimal CalculateTotal(IList<CartItem> cart)
{
decimal total = 0M;
 
foreach (CartItem item in cart)
{
total += item.quantity * item.item.Price;
}
 
return total;
}
 
private struct MenuItem
{
public string Name { get; set; }
public decimal Price { get; set; }
}
 
private struct CartItem
{
public MenuItem item { get; set; }
public decimal quantity { get; set; }
}
}
}
Output:
Total before tax: $22,000,000,000,000,005.72
Tax: $1,683,000,000,000,000.44
Total with tax: $23,683,000,000,000,006.16

Clojure[edit]

(require '[clojurewerkz.money.amounts    :as ma])
(require '[clojurewerkz.money.currencies :as mc])
(require '[clojurewerkz.money.format  :as mf])
 
(let [burgers (ma/multiply (ma/amount-of mc/USD 5.50) 4000000000000000)
milkshakes (ma/multiply (ma/amount-of mc/USD 2.86) 2)
pre-tax (ma/plus burgers milkshakes)
tax (ma/multiply pre-tax 0.0765 :up)]
(println "Total before tax: " (mf/format pre-tax))
(println " Tax: " (mf/format tax))
(println " Total with tax: " (mf/format (ma/plus pre-tax tax))))
Output:
Total before tax:  $22,000,000,000,000,005.72
             Tax:  $1,683,000,000,000,000.44
  Total with tax:  $23,683,000,000,000,006.16

COBOL[edit]

COBOL supports up to 31 digits of precision so won't need any fancy currency/BigInteger types!

Works with: GNU Cobol version 2.1
       >>SOURCE FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. currency-example.
 
DATA DIVISION.
WORKING-STORAGE SECTION.
01 Burger-Price CONSTANT 5.50.
01 Milkshake-Price CONSTANT 2.86.
01 num-burgers PIC 9(18) VALUE 4000000000000000.
01 num-milkshakes PIC 9(18) VALUE 2.
01 tax PIC 9(18)V99.
01 tax-edited PIC $(17)9.99.
01 Tax-Rate CONSTANT 7.65.
01 total PIC 9(18)V99.
01 total-edited PIC $(17)9.99.
 
PROCEDURE DIVISION.
COMPUTE total, total-edited =
num-burgers * Burger-Price + num-milkshakes * Milkshake-Price
DISPLAY "Total before tax: " total-edited
 
COMPUTE tax, tax-edited = total * (Tax-Rate / 100)
DISPLAY " Tax: " tax-edited
 
ADD tax TO total GIVING total-edited
DISPLAY " Total with tax: " total-edited
.
END PROGRAM currency-example.
Output:
Total before tax: $22000000000000005.72
             Tax:  $1683000000000000.43
  Total with tax: $23683000000000006.15

Go[edit]

package main
 
import (
"fmt"
"log"
"math/big"
)
 
// DC for dollars and cents. Value is an integer number of cents.
type DC int64
 
func (dc DC) String() string {
d := dc / 100
if dc < 0 {
dc = -dc
}
return fmt.Sprintf("%d.%02d", d, dc%100)
}
 
// Extend returns extended price of a unit price.
func (dc DC) Extend(n int) DC {
return dc * DC(n)
}
 
var one = big.NewInt(1)
var hundred = big.NewRat(100, 1)
 
// ParseDC parses dollars and cents as a string into a DC.
func ParseDC(s string) (DC, bool) {
r, ok := new(big.Rat).SetString(s)
if !ok {
return 0, false
}
r.Mul(r, hundred)
if r.Denom().Cmp(one) != 0 {
return 0, false
}
return DC(r.Num().Int64()), true
}
 
// TR for tax rate. Value is an an exact rational.
type TR struct {
*big.Rat
}
func NewTR() TR {
return TR{new(big.Rat)}
}
 
// SetString overrides Rat.SetString to return the TR type.
func (tr TR) SetString(s string) (TR, bool) {
if _, ok := tr.Rat.SetString(s); !ok {
return TR{}, false
}
return tr, true
}
 
var half = big.NewRat(1, 2)
 
// Tax computes a tax amount, rounding to the nearest cent.
func (tr TR) Tax(dc DC) DC {
r := big.NewRat(int64(dc), 1)
r.Add(r.Mul(r, tr.Rat), half)
return DC(new(big.Int).Div(r.Num(), r.Denom()).Int64())
}
 
func main() {
hamburgerPrice, ok := ParseDC("5.50")
if !ok {
log.Fatal("Invalid hamburger price")
}
milkshakePrice, ok := ParseDC("2.86")
if !ok {
log.Fatal("Invalid milkshake price")
}
taxRate, ok := NewTR().SetString("0.0765")
if !ok {
log.Fatal("Invalid tax rate")
}
 
totalBeforeTax := hamburgerPrice.Extend(4000000000000000) +
milkshakePrice.Extend(2)
tax := taxRate.Tax(totalBeforeTax)
total := totalBeforeTax + tax
 
fmt.Printf("Total before tax: %22s\n", totalBeforeTax)
fmt.Printf(" Tax: %22s\n", tax)
fmt.Printf(" Total: %22s\n", total)
}
Output:
Total before tax:   22000000000000005.72
             Tax:    1683000000000000.44
           Total:   23683000000000006.16

Haskell[edit]

import Data.Fixed
import Text.Printf
 
type Percent = Centi
type Dollars = Centi
 
tax :: Percent -> Dollars -> Dollars
tax rate = MkFixed . round . (rate *)
 
printAmount :: String -> Dollars -> IO ()
printAmount name = printf "%-10s %20s\n" name . showFixed False
 
main :: IO ()
main = do
let subtotal = 4000000000000000 * 5.50 + 2 * 2.86
tx = tax 7.65 subtotal
total = subtotal + tx
printAmount "Subtotal" subtotal
printAmount "Tax" tx
printAmount "Total" total
Output:
$ ./currency 
Subtotal   22000000000000005.72
Tax         1683000000000000.44
Total      23683000000000006.16

J[edit]

We use a naive implementation with arbitrary precision (rational) numbers:

require 'format/printf'
fmtD=: 0j2&": NB. format rational as decimal
 
Items=:  ;: 'Hamburger Milkshake'
Quantities=: x: 4000000000000000 2
Prices=: x: 5.50 2.86
Tax_rate=: x: 0.0765
Values=: Quantities * Prices
Subtotal=: +/ Values
Tax=: Tax_rate * Subtotal
Total=: Subtotal + Tax
 
OutputTemplate=: noun define
Item Price Quantity Value
%9s %8s %20d %22s
%9s %8s %20d %22s
-------------------------------
Subtotal:  %20s
Tax:  %20s
Total:  %20s
)
 
Vals=: (,Items ,. (fmtD&.> Prices) ,. (<"0 Quantities) ,. (fmtD&.> Values)) , fmtD&.> Subtotal,Tax,Total
OutputTemplate printf Vals
Output:
Item          Price            Quantity          Value
Hamburger     5.50     4000000000000000   22000000000000000.00
Milkshake     2.86                    2                   5.72
                               -------------------------------
                               Subtotal:  22000000000000005.72
                                    Tax:   1683000000000000.44
                                  Total:  23683000000000006.16

(Note that if you ever get a bill like this in real life, you should question the person who gave it to you. And, possibly consider legal action and/or patronizing a different establishment. This is because (a) you did not order that many hamburgers, and (b) they did not deliver that many hamburgers, and (c) that much hamburger probably does not exist, and ...)

Java[edit]

import java.math.*;
import java.util.*;
 
public class Currency {
final static String taxrate = "7.65";
 
enum MenuItem {
 
Hamburger("5.50"), Milkshake("2.86");
 
private MenuItem(String p) {
price = new BigDecimal(p);
}
 
public final BigDecimal price;
}
 
public static void main(String[] args) {
Locale.setDefault(Locale.ENGLISH);
 
MathContext mc = MathContext.DECIMAL128;
 
Map<MenuItem, BigDecimal> order = new HashMap<>();
order.put(MenuItem.Hamburger, new BigDecimal("4000000000000000"));
order.put(MenuItem.Milkshake, new BigDecimal("2"));
 
BigDecimal subtotal = BigDecimal.ZERO;
for (MenuItem it : order.keySet())
subtotal = subtotal.add(it.price.multiply(order.get(it), mc));
 
BigDecimal tax = new BigDecimal(taxrate, mc);
tax = tax.divide(new BigDecimal("100"), mc);
tax = subtotal.multiply(tax, mc);
 
System.out.printf("Subtotal: %20.2f%n", subtotal);
System.out.printf(" Tax: %20.2f%n", tax);
System.out.printf(" Total: %20.2f%n", subtotal.add(tax));
}
}
Subtotal: 22000000000000005.72
     Tax:  1683000000000000.44
   Total: 23683000000000006.16

JavaScript[edit]

const money = require('money-math')                                            
 
let hamburgers = 4000000000000000
let hamburgerPrice = 5.50
 
let shakes = 2
let shakePrice = 2.86
 
let tax = 7.65
 
let hamburgerTotal = money.mul(hamburgers.toFixed(0), money.floatToAmount(hamburgerPrice))
let shakeTotal = money.mul(shakes.toFixed(0), money.floatToAmount(shakePrice))
 
let subTotal = money.add(hamburgerTotal, shakeTotal)
 
let taxTotal = money.percent(subTotal, tax)
 
let total = money.add(subTotal, taxTotal)
 
console.log('Hamburger Total:', hamburgerTotal)
console.log('Shake Total:', shakeTotal)
console.log('Sub Total:', subTotal)
console.log('Tax:', taxTotal)
console.log('Total:', total)
 

Julia[edit]

Works with: Julia version 0.6
p  = [big"5.50", big"2.86"]
q = [4000000000000000, 2]
tr = big"0.0765"
 
beftax = p' * q
tax = beftax * tr
afttax = beftax + tax
 
@printf " - tot. before tax: %20.2f \$\n" beftax
@printf " - tax: %20.2f \$\n" tax
@printf " - tot. after tax: %20.2f \$\n" afttax
Output:
 - tot. before tax: 22000000000000005.72 $
 -             tax:  1683000000000000.44 $
 - tot. after  tax: 23683000000000006.16 $

Kotlin[edit]

// version 1.1.2
 
import java.math.BigDecimal
import java.math.MathContext
 
fun main(args: Array<String>) {
val mc = MathContext.DECIMAL128
val nHamburger = BigDecimal("4000000000000000", mc)
val pHamburger = BigDecimal("5.50")
val nMilkshakes = BigDecimal("2", mc)
val pMilkshakes = BigDecimal("2.86")
val taxRate = BigDecimal("0.0765")
val price = nHamburger * pHamburger + nMilkshakes * pMilkshakes
val tax = price * taxRate
val fmt = "%20.2f"
println("Total price before tax : ${fmt.format(price)}")
println("Tax thereon @ 7.65%  : ${fmt.format(tax)}")
println("Total price after tax  : ${fmt.format(price + tax)}")
}
Output:
Total price before tax : 22000000000000005.72
Tax thereon @ 7.65%    :  1683000000000000.44
Total price after tax  : 23683000000000006.16

Perl 6[edit]

Works with: Rakudo version 2016.01

No need for a special type in Perl 6, since the Rat type is used for normal fractions. (In order to achieve imprecision, you have to explicitly use scientific notation, or use the Num type, or calculate a result that requires a denominator in excess of 2 ** 64. (There's no limit on the numerator.))

my @check = q:to/END/.lines.map: { [.split(/\s+/)] };
Hamburger 5.50 4000000000000000
Milkshake 2.86 2
END
 
my $tax-rate = 0.0765;
 
my $fmt = "%-10s %8s %18s %22s\n";
 
printf $fmt, <Item Price Quantity Extension>;
 
my $subtotal = [+] @check.map: -> [$item,$price,$quant] {
my $extension = $price * $quant;
printf $fmt, $item, $price, $quant, fix2($extension);
$extension;
}
 
printf $fmt, '', '', '', '-----------------';
printf $fmt, '', '', 'Subtotal ', $subtotal;
 
my $tax = ($subtotal * $tax-rate).round(0.01);
printf $fmt, '', '', 'Tax ', $tax;
 
my $total = $subtotal + $tax;
printf $fmt, '', '', 'Total ', $total;
 
# make up for lack of a Rat fixed-point printf format
sub fix2($x) { ($x + 0.001).subst(/ <?after \.\d\d> .* $ /, '') }
Output:
Item          Price           Quantity              Extension
Hamburger      5.50   4000000000000000   22000000000000000.00
Milkshake      2.86                  2                   5.72
                                            -----------------
                             Subtotal    22000000000000005.72
                                  Tax     1683000000000000.44
                                Total    23683000000000006.16

Python[edit]

This uses Pythons decimal module, (and some copying of names from the Perl 6 example).

from decimal import Decimal as D
from collections import namedtuple
 
Item = namedtuple('Item', 'price, quant')
 
items = dict( hamburger=Item(D('5.50'), D('4000000000000000')),
milkshake=Item(D('2.86'), D('2')) )
tax_rate = D('0.0765')
 
fmt = "%-10s %8s %18s %22s"
print(fmt % tuple('Item Price Quantity Extension'.upper().split()))
 
total_before_tax = 0
for item, (price, quant) in sorted(items.items()):
ext = price * quant
print(fmt % (item, price, quant, ext))
total_before_tax += ext
print(fmt % ('', '', '', '--------------------'))
print(fmt % ('', '', 'subtotal', total_before_tax))
 
tax = (tax_rate * total_before_tax).quantize(D('0.00'))
print(fmt % ('', '', 'Tax', tax))
 
total = total_before_tax + tax
print(fmt % ('', '', '', '--------------------'))
print(fmt % ('', '', 'Total', total))
Output:
ITEM          PRICE           QUANTITY              EXTENSION
hamburger      5.50   4000000000000000   22000000000000000.00
milkshake      2.86                  2                   5.72
                                         --------------------
                              subtotal   22000000000000005.72
                                   Tax    1683000000000000.44
                                         --------------------
                                 Total   23683000000000006.16

Racket[edit]

Racket can handle fractions. To read the decimals numbers as fractions instead of floating point numbers, they must start with #e. For example #e.3 -> 3/10 and .3 ->3E-1. The main problem is rounding correctly and this part is handled in cents-*, that implements a multiplication that rounds to the cents. The rest of the program is only formatting.

#lang racket
(define (cents-* x y)
(/ (round (* 100 x y)) 100))
 
(struct item (name count price))
 
(define (string-pad-right len . strs)
(define all (apply string-append strs))
(string-append all (make-string (- len (string-length all)) #\space)))
 
(define (string-pad-left len . strs)
(define all (apply string-append strs))
(string-append (make-string (- len (string-length all)) #\space) all))
 
(define (show-formated name count price total)
(printf "~a ~a ~a -> ~a\n"
(string-pad-right 10 name)
(string-pad-left 18 count)
(string-pad-left 8 price)
(string-pad-left 23 total)
))
 
(define (show-item it)
(show-formated (item-name it)
(~r (item-count it))
(string-append "$" (~r (item-price it) #:precision '(= 2)))
(string-append "$" (~r (cents-* (item-count it) (item-price it)) #:precision '(= 2)))
))
 
(define (show-total all tax-rate)
(define net (for/sum ([it (in-list all)])
(cents-* (item-count it) (item-price it))))
(define tax (cents-* net tax-rate))
(show-formated "" "" "net" (string-append "$" (~r net #:precision '(= 2))))
(show-formated "" "" "tax" (string-append "$" (~r tax #:precision '(= 2))))
(show-formated "" "" "total" (string-append "$" (~r (+ net tax) #:precision '(= 2))))
)
 
(define hamburger (item "hamburger" 4000000000000000 #e5.50))
(define milkshake (item "milkshake" 2 #e2.86))
(define all (list hamburger milkshake))
 
(for-each show-item all)
(newline)
(show-total all (/ #e7.65 100))
Output:
hamburger    4000000000000000    $5.50 ->   $22000000000000000.00
milkshake                   2    $2.86 ->                   $5.72

                                   net ->   $22000000000000005.72
                                   tax ->    $1683000000000000.44
                                 total ->   $23683000000000006.16

REXX[edit]

REXX uses characters to represent everything, including all forms of numbers.   So what is expressed as a literal (characters) is what REXX uses.   Essentially, it can be thought of as decimal.

Programming note:   the tax rate can be expressed with or without a percent   (%)  suffix.

without commas[edit]

/*REXX program shows a method of computing the total price and tax  for purchased items.*/
numeric digits 200 /*support for gihugic numbers.*/
taxRate= 7.65 /*number is: nn or nn% */
if right(taxRate, 1)\=='%' then taxRate=taxRate / 100 /*handle plain tax rate number*/
taxRate=strip(taxRate, , '%') /*strip the  % (if present).*/
item. =; items=0 /*zero out the register. */
item.1 = '4000000000000000 $5.50 hamburger' /*the first item purchased. */
item.2 = ' 2 $2.86 milkshake' /* " second " " */
say center('quantity',22) center("item",22) center('price',22)
hdr=center('', 27,"─") center('', 20,"─") center('', 27,"─"); say hdr
total=0
do j=1 while item.j\=='' /*calculate the total and tax.*/
parse var item.j quantity price thing /*ring up an item on register.*/
items=items+quantity /*tally the number of items. */
price = translate(price,,'$') /*maybe scrub out the $ symbol*/
subtotal = quantity * price /*calculate the sub-total.*/
total = total + subtotal /* " " running total.*/
say right(quantity, 27) left(thing, 20) show$(subtotal)
end /*j*/
say /*display a blank line for separator. */
say translate(hdr, '═', "─") /*display the separator part of the hdr*/
tax=format(total*taxRate,,2) /*round the total tax for all the items*/
say right(items "(items)",35) right('total=',12) show$(total)
say right('tax at' (taxRate*100/1)"%=", 48) show$(tax)
say
say right('grand total=', 48) show$(total+tax)
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
show$: return right(commas('$' || arg(1)),27) /*right─justify and format a number. */

output   (attempting to mimic a check-out register to some degree):

       quantity                 item                  price
────────────────────── ────────────────────── ──────────────────────
      4000000000000000 hamburger               $22000000000000000.00
                     2 milkshake                               $5.72

══════════════════════ ══════════════════════ ══════════════════════
      4000000000000002 (items)         total=  $22000000000000005.72
                                tax at 7.65%=   $1683000000000000.44

                                 grand total=  $23683000000000006.16

with commas[edit]

/*REXX program shows a method of computing the total price and tax  for purchased items.*/
numeric digits 200 /*support for gihugic numbers.*/
taxRate= 7.65 /*number is: nn or nn% */
if right(taxRate, 1)\=='%' then taxRate=taxRate / 100 /*handle plain tax rate number*/
taxRate=strip(taxRate,,'%') /*strip the  % (if present).*/
item. =; items=0 /*zero out the register. */
item.1 = '4000000000000000 $5.50 hamburger' /*the first item purchased. */
item.2 = ' 2 $2.86 milkshake' /* " second " " */
say center('quantity',22) center("item",22) center('price',22)
hdr=center('', 27,"─") center('', 20,"─") center('', 27,"─"); say hdr
total=0
do j=1 while item.j\=='' /*calculate the total and tax.*/
parse var item.j quantity price thing /*ring up an item on register.*/
items=items+quantity /*tally the number of items. */
price = translate(price, , '$') /*maybe scrub out the $ symbol*/
subtotal = quantity * price /*calculate the sub-total.*/
total = total + subtotal /* " " running total.*/
say right(quantity, 27) left(thing, 20) show$(subtotal)
end /*j*/
say /*display a blank line for separator. */
say translate(hdr, '═', "─") /*display the separator part of the hdr*/
tax=format(total*taxRate,,2) /*round the total tax for all the items*/
say right(commas(items "(items)"),35) right('total=',12) show$(total)
say right('tax at' (taxRate*100/1)"%=", 48) show$(tax)
say
say right('grand total=', 48) show$(total+tax)
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
commas: procedure; parse arg _; n=_'.9'; #=123456789; b=verify(n,#,"M")
e=verify(n,#'0',,verify(n,#"0.",'M'))-4
do j=e to b by -3; _=insert(',',_,j); end /*j*/; return _
/*──────────────────────────────────────────────────────────────────────────────────────*/
show$: return right(commas('$' || arg(1)),27) /*right─justify and format a number. */

output   with commas in the larger numbers:

         quantity                   item                    price
─────────────────────────── ──────────────────── ───────────────────────────
      4,000,000,000,000,000 hamburger             $22,000,000,000,000,000.00
                          2 milkshake                                  $5.72

═══════════════════════════ ════════════════════ ═══════════════════════════
      4,000,000,000,000,002 (items)       total=  $22,000,000,000,000,005.72
                                   tax at 7.65%=   $1,683,000,000,000,000.44

                                    grand total=  $23,683,000,000,000,006.16

Scala[edit]

Library: Scala
Locale is manipulated to demonstrate the behavior with other currencies.
import java.text.NumberFormat
import java.util.Locale
 
object SizeMeUp extends App {
 
val menu: Map[String, (String, Double)] = Map("burg" ->("Hamburger XL", 5.50), "milk" ->("Milkshake", 2.86))
val order = List((4000000000000000L, "burg"), (2L, "milk"))
 
Locale.setDefault(new Locale("ru", "RU"))
 
val (currSymbol, tax) = (NumberFormat.getInstance().getCurrency.getSymbol, 0.0765)
 
def placeOrder(order: List[(Long, String)]) = {
val totals = for ((qty, article) <- order) yield {
val (desc, itemPrize) = menu(article)
val (items, post) = (qty, qty * BigDecimal(itemPrize))
println(f"$qty%16d\t$desc%-16s\t$currSymbol%4s$itemPrize%6.2f\t$post%,25.2f")
(items, post)
}
totals.foldLeft((0L, BigDecimal(0))) { (acc, n) => (acc._1 + n._1, acc._2 + n._2)}
}
 
val (items, beforeTax) = placeOrder(order)
 
println(f"$items%16d\t${"ordered items"}%-16s${'\t' + " Subtotal" + '\t'}$beforeTax%,25.2f")
 
val taxation = beforeTax * tax
println(f"${" " * 16 + '\t' + " " * 16 + '\t' + f"${tax * 100}%5.2f%% tax" + '\t'}$taxation%,25.2f")
println(f"${" " * 16 + '\t' + " " * 16 + '\t' + "Amount due" + '\t'}${beforeTax + taxation}%,25.2f")
}
Output:
4000000000000000	Hamburger XL    	руб.  5,50	22 000 000 000 000 000,00
               2	Milkshake       	руб.  2,86	                     5,72
4000000000000002	ordered items   	  Subtotal	22 000 000 000 000 005,72
                	                	 7,65% tax	 1 683 000 000 000 000,44
                	                	Amount due	23 683 000 000 000 006,16

Process finished with exit code 0

Sidef[edit]

Translation of: Perl 6
struct Item {
name, price, quant
}
 
var check = %q{
Hamburger 5.50 4000000000000000
Milkshake 2.86 2
}.lines.grep(/\S/).map { Item(.words...) }
 
var tax_rate = 0.0765
var fmt = "%-10s %8s %18s %22s\n"
 
printf(fmt, %w(Item Price Quantity Extension)...)
 
var subtotal = check.map { |item|
var extension = Num(item.price)*Num(item.quant)
printf(fmt, item.name, item.price, item.quant, extension.round(-2))
extension
}.sum(0)
 
printf(fmt, '', '', '', '-----------------')
printf(fmt, '', '', 'Subtotal ', subtotal)
 
var tax = (subtotal * tax_rate -> round(-2))
printf(fmt, '', '', 'Tax ', tax)
 
var total = subtotal+tax
printf(fmt, '', '', 'Total ', total)
Output:
Item          Price           Quantity              Extension
Hamburger      5.50   4000000000000000      22000000000000000
Milkshake      2.86                  2                   5.72
                                            -----------------
                             Subtotal    22000000000000005.72
                                  Tax     1683000000000000.44
                                Total    23683000000000006.16

Tcl[edit]

Library: Tcllib (Package: math::decimal)
package require math::decimal
namespace import math::decimal::*
 
set hamburgerPrice [fromstr 5.50]
set milkshakePrice [fromstr 2.86]
set taxRate [/ [fromstr 7.65] [fromstr 100]]
 
set burgers 4000000000000000
set shakes 2
set net [+ [* [fromstr $burgers] $hamburgerPrice] [* [fromstr $shakes] $milkshakePrice]]
set tax [round_up [* $net $taxRate] 2]
set total [+ $net $tax]
 
puts "net=[tostr $net], tax=[tostr $tax], total=[tostr $total]"
Output:
net=22000000000000005.72, tax=1683000000000000.44, total=23683000000000006.16

zkl[edit]

zkl Ints are 64 bits, so we have 18 digits to play with. So, just multiply bucks by 100 and be careful when doing tax calculations.

Translation of: Python
var priceList=Dictionary("hamburger",550, "milkshake",286);
var taxRate=765; // percent*M
const M=0d10_000;
 
fcn toBucks(n){ "$%,d.%02d".fmt(n.divr(100).xplode()) }
fcn taxIt(n) { d,c:=n.divr(M).apply('*(taxRate)); d + (c+5000)/M; }
fcn calcTab(items){ // (hamburger,15), (milkshake,100) ...
items=vm.arglist;
fmt:="%-10s %8s %18s %26s";
fmt.fmt("Item Price Quantity Extension".split().xplode()).println();
 
totalBeforeTax:=0;
foreach item,n in (items.sort(fcn(a,b){ a[0]<b[0] })){
price:=priceList[item]; t:=price*n;
fmt.fmt(item,toBucks(price),n,toBucks(t)).println();
totalBeforeTax+=t;
}
fmt.fmt("","","","--------------------").println();
fmt.fmt("","","subtotal",toBucks(totalBeforeTax)).println();
 
tax:=taxIt(totalBeforeTax);
fmt.fmt("","","Tax",toBucks(tax)).println();
 
fmt.fmt("","","","--------------------").println();
fmt.fmt("","","Total",toBucks(totalBeforeTax + tax)).println();
}
calcTab(T("milkshake",2),T("hamburger",4000000000000000));
Output:
Item          Price           Quantity                  Extension
hamburger     $5.50   4000000000000000 $22,000,000,000,000,000.00
milkshake     $2.86                  2                      $5.72
                                             --------------------
                              subtotal $22,000,000,000,000,005.72
                                   Tax  $1,683,000,000,000,000.44
                                             --------------------
                                 Total $23,683,000,000,000,006.16