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       (four quadrillion burgers)
 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.




use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
use framework "Foundation"
 
-- Derive an NSDecimalNumber from an AppleScript number or numeric text.
-- NSDecimalNumbers also allow arithmetic and have a far greater range than AS numbers.
on decimalNumberFrom(n)
    return current application's class "NSDecimalNumber"'s decimalNumberWithString:(n as text)
end decimalNumberFrom
 
-- Multiply two NSDecimalNumbers.
on multiply(dn1, dn2)
    return dn1's decimalNumberByMultiplyingBy:(dn2)
end multiply
 
-- Add two NSDecimalNumbers.
on add(dn1, dn2)
    return dn1's decimalNumberByAdding:(dn2)
end add
 
on billTotal(quantitiesAndPrices, taxRate, currencySymbol)
    -- Set up an NSNumberFormatter for converting between currency strings and NSDecimalNumbers.
    set currencyFormatter to current application's class "NSNumberFormatter"'s new()
    tell currencyFormatter to setNumberStyle:(current application's NSNumberFormatterCurrencyStyle)
    tell currencyFormatter to setCurrencySymbol:(currencySymbol)
    tell currencyFormatter to setGeneratesDecimalNumbers:(true)
 
    -- Tot up the bill from the list of quantities (numbers or numeric strings) and unit prices (currency strings with symbols).
    set subtotal to decimalNumberFrom(0) -- or:  current application's class "NSDecimalNumber"'s zero()
    repeat with thisEntry in quantitiesAndPrices
        set {quantity:quantity, unitPrice:unitPrice} to thisEntry
        set entryTotal to multiply(decimalNumberFrom(quantity), currencyFormatter's numberFromString:(unitPrice))
        set subtotal to add(subtotal, entryTotal)
    end repeat
    -- Work out the tax and add it to the subtotal.
    set tax to multiply(subtotal, decimalNumberFrom(taxRate / 100))
    set total to add(subtotal, tax)
 
    -- Format and return the results.
    return (current application's class "NSString"'s stringWithFormat_("Subtotal:  %@
Tax:  %@
Total:  %@", ¬
        currencyFormatter's stringFromNumber:(subtotal), ¬
        currencyFormatter's stringFromNumber:(tax), ¬
        currencyFormatter's stringFromNumber:(total))) ¬
        as text
end billTotal
 
-- Demo code:
set currencySymbol to "$"
set quantitiesAndPrices to {{quantity:"4000000000000000", unitPrice:currencySymbol & "5.50"}, ¬
    {quantity:2, unitPrice:currencySymbol & 2.86}}
set taxRate to 7.65
return billTotal(quantitiesAndPrices, taxRate, currencySymbol)