Currency: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|REXX}}: added the REXX language. -- ~~~~)
(→‎{{header|Tcl}}: Update from task change)
Line 205: Line 205:


set hamburgerPrice [fromstr 5.50]
set hamburgerPrice [fromstr 5.50]
set milkshakePrice [fromstr 2.96]
set milkshakePrice [fromstr 2.86]
set taxRate [/ [fromstr 7.65] [fromstr 100]]
set taxRate [/ [fromstr 7.65] [fromstr 100]]


set burgers 4000000000000000
set net [+ [* [fromstr 4] $hamburgerPrice] [* [fromstr 2] $milkshakePrice]]
set shakes 2
set net [+ [* [fromstr $burgers] $hamburgerPrice] [* [fromstr $shakes] $milkshakePrice]]
set tax [round_up [* $net $taxRate] 2]
set tax [round_up [* $net $taxRate] 2]
set total [+ $net $tax]
set total [+ $net $tax]
Line 215: Line 217:
{{out}}
{{out}}
<pre>
<pre>
net=27.92, tax=2.14, total=30.06
net=22000000000000005.72, tax=1683000000000000.44, total=23683000000000006.16
</pre>
</pre>

Revision as of 22:43, 4 January 2014

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.

Show how you might represent currency in a simple example, using a data type that represent exact values of dollars and cents. Note for example that 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's 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 the total price before tax, the tax, and the total with tax, and show results on this page. 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, and 23683000000000006.16. Dollar signs and thousands separators are optional.

Go

<lang go>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)

}</lang>

Output:
Total before tax:   22000000000000005.72
             Tax:    1683000000000000.44
           Total:   23683000000000006.16

J

<lang j>require 'numeric format/printf' hamburger_price=: 5.50 milkshake_price=: 2.96 tax_rate=: 0.0765

total_before_tax=: +/ 4 2 * hamburger_price , milkshake_price tax=: 0.01 round tax_rate * total_before_tax total=: total_before_tax + tax

(8!:0 total_before_tax, tax, total) printf~ noun define

   Total before tax: %8s
                Tax: %8s
              Total: %8s

)</lang>

Output:
    Total before tax:    27.92
                 Tax:     2.14
               Total:    30.06

Perl 6

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.)) <lang perl6>my $hamburger-price = 5.50; my $milkshake-price = 2.96; my $tax-rate = 0.0765;

my $total-before-tax = $hamburger-price * 4 + $milkshake-price * 2; my $tax = ($tax-rate * $total-before-tax).round(0.01); my $total = $total-before-tax + $tax;

say qq:to/END/;

   Total before tax: $total-before-tax.fmt('%8s')
                Tax: $tax.fmt('%8s')
              Total: $total.fmt('%8s')
   END</lang>
Output:
Total before tax:    27.92
	     Tax:     2.14
	   Total:    30.06

Python

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

<lang python>from decimal import Decimal as D

hamburger_price = D('5.50') milkshake_price = D('2.96') tax_rate = D('0.0765')

total_before_tax = hamburger_price * 4 + milkshake_price * 2 tax = D(round(tax_rate * total_before_tax, 2)) total = total_before_tax + tax

print(

   Total before tax: %8s
                Tax: %8s
              Total: %8s

 % tuple(round(x, 2) for x in (total_before_tax, tax, total)))</lang>

Output:
    Total before tax:    27.92
                 Tax:     2.14
               Total:    30.06

REXX

<lang rexx>/*REXX pgm shows a method of computing the total price & tax for itemss */ taxRate= 7.65 /*current tax rate.*/ if right(taxRate,1)\=='%' then taxRate=taxRate/100 /*handle plain tax.*/ taxRate=strip(taxRate,,'%') /*strip % (if any)*/ item. =; items=0 /*zero out register*/ item.1 = '4 $5.50 hamburger' item.2 = '2 $2.96 milkshake' total=0

        do j=1  while item.j\==     /*for all items, calc. total,tax.*/
        parse var item.j   quantity price thing   /*ring up an item.   */
        items=items+quantity                      /*tally the # items. */
        price    = translate(price,,'$')          /*maybe scrub out $. */
        subtotal = quantity * price               /*calculate subtotal.*/
        total    = total + subtotal               /*calc. running total*/
        say right(quantity,4) left(thing,20) show$(subtotal)
        end   /*j*/

say '─────────────────────────────────────' /*show separator line*/ tax=format(total*taxRate,,2) /*round total tax for all items. */ say right(items "(items)",12) right('total=',12) show$(total) say right('tax at' (taxRate*100/1)"%=", 25) show$(tax) say right('grand total=', 25) show$(total+tax) exit /*stick a fork in it, we're done.*/ /*──────────────────────────────────SHOW$ subroutine────────────────────*/ show$: return right('$' || arg(1),9) /*right-justify & format a $total*/</lang> output   (attempting to mimic a check-out register to some degree):

   4 hamburger               $22.00
   2 milkshake                $5.92
─────────────────────────────────────
   6 (items)       total=    $27.92
            tax at 7.65%=     $2.14
             grand total=    $30.06

Tcl

Library: Tcllib (Package: math::decimal)

<lang tcl>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]"</lang>

Output:
net=22000000000000005.72, tax=1683000000000000.44, total=23683000000000006.16