Currency: Difference between revisions
(Corrected grammar.) |
(added RPL) |
||
(92 intermediate revisions by 41 users not shown) | |||
Line 1: | Line 1: | ||
{{ |
{{task}} |
||
;Task: |
;Task: |
||
Line 11: | Line 11: | ||
Use the values: |
Use the values: |
||
::* 4000000000000000 hamburgers at $5.50 each |
::* 4000000000000000 hamburgers at $5.50 each (four quadrillion burgers) |
||
::* 2 milkshakes at $2.86 each, and |
::* 2 milkshakes at $2.86 each, and |
||
::* a tax rate of 7.65%. |
::* a tax rate of 7.65%. |
||
Line 36: | Line 36: | ||
Dollar signs and thousands separators are optional. |
Dollar signs and thousands separators are optional. |
||
<br><br> |
<br><br> |
||
=={{header|11l}}== |
|||
{{trans|Nim}} |
|||
<syntaxhighlight lang="11l">F currency(units, subunits) |
|||
R BigInt(units) * 100 + subunits |
|||
F currency_from_str(s) |
|||
V (units, subunits) = s.split(‘.’) |
|||
R BigInt(units) * 100 + Int(subunits) |
|||
F percentage(a, num, denom) |
|||
R (a * num * 10 I/ denom + 5) I/ 10 |
|||
F to_str(c) |
|||
R String(c I/ 100)‘’‘.#02’.format(c % 100) |
|||
V hamburgers = currency(5, 50) * 4'000'000'000'000'000 |
|||
V milkshakes = currency_from_str(‘2.86’) * 2 |
|||
V beforeTax = hamburgers + milkshakes |
|||
V tax = percentage(beforeTax, 765, 10'000) |
|||
V total = beforeTax + tax |
|||
V maxlen = max(to_str(beforeTax).len, to_str(tax).len, to_str(total).len) |
|||
print(‘Total price before tax: ’to_str(beforeTax).rjust(maxlen)) |
|||
print(‘Tax: ’to_str(tax).rjust(maxlen)) |
|||
print(‘Total with tax: ’to_str(total).rjust(maxlen))</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Total price before tax: 22000000000000005.72 |
|||
Tax: 1683000000000000.44 |
|||
Total with tax: 23683000000000006.16 |
|||
</pre> |
|||
=={{header|Ada}}== |
|||
Numeric constants in Ada do not have to be given an explicit type. When used in constant expressions together, they have |
|||
arbitrary precision. This enables simple calculation of the values. Ada also has builtin fixed-point |
|||
types, which can define a minimum interval between decimal values (in this case, 1 cent or $0.01). |
|||
When using the Dollar_IO package to print each value, the constant is converted into a Dollar, then printed. All of |
|||
the arbitrary-precision arithmetic operations are done at compile-time, incurring no runtime cost. |
|||
<syntaxhighlight lang="ada">with Ada.Text_IO; |
|||
procedure Currency is |
|||
type Dollar is delta 0.01 range 0.0 .. 24_000_000_000_000_000.0; |
|||
package Dollar_IO is new Ada.Text_IO.Fixed_IO(Dollar); |
|||
hamburger_cost : constant := 5.50; |
|||
milkshake_cost : constant := 2.86; |
|||
tax_rate : constant := 0.0765; |
|||
total_cost : constant := hamburger_cost * 4_000_000_000_000_000.0 + milkshake_cost * 2; |
|||
total_tax : constant := total_cost * tax_rate; |
|||
total_with_tax : constant := total_cost + total_tax; |
|||
begin |
|||
Ada.Text_IO.Put("Price before tax:"); |
|||
Dollar_IO.Put(total_cost); |
|||
Ada.Text_IO.New_Line; |
|||
Ada.Text_IO.Put("Tax: "); |
|||
Dollar_IO.Put(total_tax); |
|||
Ada.Text_IO.New_Line; |
|||
Ada.Text_IO.Put("Total: "); |
|||
Dollar_IO.Put(total_with_tax); |
|||
Ada.Text_IO.New_Line; |
|||
end Currency;</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Price before tax: 22000000000000005.72 |
|||
Tax: 1683000000000000.44 |
|||
Total: 23683000000000006.16 |
|||
</pre> |
|||
=={{header|ALGOL 68}}== |
|||
{{works with|ALGOL 68G|Any - tested with release 2.8.3.win32}} |
|||
<syntaxhighlight lang="algol68">BEGIN |
|||
# currency calculations # |
|||
# simple fixed point type, LONG INT is 64-bit in Algol 68G # |
|||
MODE FIXED = STRUCT( LONG INT value |
|||
, INT decimals |
|||
, INT fraction modulus |
|||
); |
|||
# make CURRENCY a synonym for FIXED # |
|||
MODE CURRENCY = FIXED; |
|||
# dyadic operator so we can write e.g. 5 DOLLARS 50 to construct # |
|||
# a CURRENCY value with 2 decimal places # |
|||
PRIO DOLLARS = 9; |
|||
OP DOLLARS = ( LONG INT v, INT dp )CURRENCY: ( ( v * 100 ) + dp, 2, 100 ); |
|||
OP DOLLARS = ( INT v, INT dp )CURRENCY: LENG v DOLLARS dp; |
|||
# issues an error message and stops the program if a has a different # |
|||
# number of decimal places to b # |
|||
PROC check compatible = ( CURRENCY a, b )VOID: |
|||
IF decimals OF a /= decimals OF b THEN print( ( "Incompatible CURRENCY values", newline ) ); stop FI; |
|||
# operators to multiply CURRENCY values by integers # |
|||
OP * = ( CURRENCY v, LONG INT m )CURRENCY: ( value OF v * m, decimals OF v, fraction modulus OF v ); |
|||
OP * = ( CURRENCY v, INT m )CURRENCY: v * LENG m; |
|||
# returns the CURRENCY value a + b # |
|||
OP + = ( CURRENCY a, CURRENCY b )CURRENCY: |
|||
BEGIN |
|||
check compatible( a, b ); |
|||
( value OF a + value OF b, decimals OF a, fraction modulus OF a ) |
|||
END # + # ; |
|||
# multiplies the CURRENCY value a by the FIXED value b, # |
|||
# rounding the result to the decimal places of a # |
|||
OP * = ( CURRENCY a, FIXED b )CURRENCY: |
|||
BEGIN |
|||
LONG INT result := ( value OF a * value OF b ); |
|||
IF decimals OF b > 0 THEN |
|||
INT d = fraction modulus OF b; |
|||
LONG INT abs result := ABS result; |
|||
INT extra places = SHORTEN ( abs result MOD d ); |
|||
abs result OVERAB d; |
|||
IF extra places >= d OVER 2 THEN abs result +:= 1 FI; |
|||
IF result < 0 THEN result := - abs result ELSE result := abs result FI |
|||
FI; |
|||
( result, decimals OF a, fraction modulus OF a ) |
|||
END # * # ; |
|||
# converts a FIXED value to a STRING with the appropriate number of # |
|||
# decimal places # |
|||
OP TOSTRING = ( FIXED v )STRING: |
|||
IF decimals OF v < 1 THEN |
|||
whole( value OF v, 0 ) |
|||
ELSE |
|||
INT d = fraction modulus OF v; |
|||
STRING result := whole( value OF v OVER d, 0 ); |
|||
STRING dp := whole( ( ABS value OF v ) MOD d, - decimals OF v ); |
|||
FOR i FROM LWB dp TO UPB dp DO IF dp[ i ] = " " THEN dp[ i ] := "0" FI OD; |
|||
result + "." + dp |
|||
FI # TOSTRING # ; |
|||
# Task calculations # |
|||
CURRENCY hb = 5 DOLLARS 50 * LONG 4000000000000000; |
|||
CURRENCY ms = 2 DOLLARS 86 * 2; |
|||
FIXED rate = ( 765, 4, 10 000 ); # 0.0765 # |
|||
CURRENCY net = hb + ms; |
|||
CURRENCY tax = net * rate; |
|||
CURRENCY total = net + tax; |
|||
print( ( "before tax: ", TOSTRING net, newline ) ); |
|||
print( ( "tax: ", TOSTRING tax, newline ) ); |
|||
print( ( "total: ", TOSTRING total, newline ) ) |
|||
END</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
before tax: 22000000000000005.72 |
|||
tax: 1683000000000000.44 |
|||
total: 23683000000000006.16 |
|||
</pre> |
|||
=={{header|AppleScript}}== |
|||
The AppleScript core language doesn't recognise currency values specifically and its numbers don't have the precision required for this task, but through ASObjC, it's able to use Foundation classes which do. |
|||
<syntaxhighlight lang="applescript">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)</syntaxhighlight> |
|||
{{output}} |
|||
<syntaxhighlight lang="applescript">"Subtotal: $22,000,000,000,000,005.72 |
|||
Tax: $1,683,000,000,000,000.44 |
|||
Total: $23,683,000,000,000,006.16"</syntaxhighlight> |
|||
=={{header|AWK}}== |
=={{header|AWK}}== |
||
===version 1=== |
===version 1=== |
||
<syntaxhighlight lang="awk"> |
|||
<lang AWK> |
|||
# syntax: GAWK -M -f CURRENCY.AWK |
# 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) |
# using GNU Awk 4.1.1, API: 1.1 (GNU MPFR 3.1.2, GNU MP 5.1.2) |
||
Line 60: | Line 272: | ||
exit(0) |
exit(0) |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
<p>Output:</p> |
<p>Output:</p> |
||
<pre> |
<pre> |
||
Line 72: | Line 284: | ||
</pre> |
</pre> |
||
===version 2=== |
===version 2=== |
||
<syntaxhighlight lang="awk"> |
|||
<lang AWK> |
|||
# syntax: GAWK -M -f CURRENCY2.AWK |
# 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) |
# using GNU Awk 4.1.1, API: 1.1 (GNU MPFR 3.1.2, GNU MP 5.1.2) |
||
Line 96: | Line 308: | ||
exit(0) |
exit(0) |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
<p>Output:</p> |
<p>Output:</p> |
||
<pre> |
<pre> |
||
Line 107: | Line 319: | ||
total 23683000000000006.16 |
total 23683000000000006.16 |
||
</pre> |
</pre> |
||
=={{header|BBC BASIC}}== |
|||
{{works with|BBC BASIC for Windows}} |
|||
<syntaxhighlight lang="bbcbasic"> REM No need for BigNum library. |
|||
REM This language uses 80bit (10 bytes!) for real values internally. |
|||
Price = 4E15 * 5.50 + 2.0 * 2.86 |
|||
Tax = Price * .0765 |
|||
Total = Price + Tax |
|||
REM Number printing will use 2 decimal places and 21 positions zone |
|||
@%=&020215 |
|||
PRINT "Price = $" Price |
|||
PRINT "Tax = $" Tax |
|||
PRINT "Total = $" Total</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Price = $ 22000000000000005.72 |
|||
Tax = $ 1683000000000000.44 |
|||
Total = $ 23683000000000006.16</pre> |
|||
=={{header|Bracmat}}== |
|||
The amounts <code>before-tax</code>, <code>tax</code>, <code>after-tax</code> are computed as rounded amounts in dollar cents. |
|||
<syntaxhighlight lang="bracmat"> div$((4000000000000000*550+2*286)+1/2,1):?before-tax |
|||
& div$(!before-tax*765/10000+1/2,1):?tax |
|||
& !before-tax+!tax:?after-tax |
|||
& ( fix |
|||
= cents dollars |
|||
. mod$(!arg.100):?cents |
|||
& ( !cents:<10&0 !cents:?cents |
|||
| |
|||
) |
|||
& div$(!arg.100):?dollars |
|||
& str$(!dollars "." !cents) |
|||
) |
|||
& str |
|||
$ ( "before-tax " |
|||
fix$!before-tax |
|||
"\ntax " |
|||
fix$!tax |
|||
\n |
|||
"after-tax " |
|||
fix$!after-tax |
|||
\n |
|||
)</syntaxhighlight> |
|||
''Output'' |
|||
<pre>before-tax 22000000000000005.72 |
|||
tax 1683000000000000.44 |
|||
after-tax 23683000000000006.16</pre> |
|||
=={{header|C}}== |
=={{header|C}}== |
||
Line 116: | Line 374: | ||
</pre> |
</pre> |
||
One remark about the code, notice that for setting all other variables the '''mpf_set_d''' function is used: |
One remark about the code, notice that for setting all other variables the '''mpf_set_d''' function is used: |
||
<syntaxhighlight lang="c"> |
|||
<lang C> |
|||
mpf_set_d(burgerUnitPrice,5.50); |
mpf_set_d(burgerUnitPrice,5.50); |
||
mpf_set_d(milkshakePrice,2 * 2.86); |
mpf_set_d(milkshakePrice,2 * 2.86); |
||
mpf_set_d(burgerNum,4000000000000000); |
mpf_set_d(burgerNum,4000000000000000); |
||
mpf_set_d(milkshakeNum,2); |
mpf_set_d(milkshakeNum,2); |
||
</syntaxhighlight> |
|||
</lang> |
|||
But when it comes to the tax rate, it's '''mpf_set_str''': |
But when it comes to the tax rate, it's '''mpf_set_str''': |
||
<syntaxhighlight lang="c"> |
|||
<lang C> |
|||
mpf_set_str(tax,"0.0765",10); |
mpf_set_str(tax,"0.0765",10); |
||
</syntaxhighlight> |
|||
</lang> |
|||
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 :) |
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 :) |
||
{{libheader|GMP}} |
{{libheader|GMP}} |
||
<syntaxhighlight lang="c"> |
|||
<lang C> |
|||
/*Abhishek Ghosh, 8th November 2017*/ |
|||
#include<stdio.h> |
#include<stdio.h> |
||
Line 160: | Line 417: | ||
return 0; |
return 0; |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
Output: |
Output: |
||
<pre> |
<pre> |
||
Line 168: | Line 425: | ||
</pre> |
</pre> |
||
=={{header|C sharp}}== |
=={{header|C sharp|C#}}== |
||
The built in C# type decimal has a max value of 79228162514264337593543950335. |
The built in C# type decimal has a max value of 79228162514264337593543950335. |
||
< |
<syntaxhighlight lang="csharp">using System; |
||
using System.Collections.Generic; |
using System.Collections.Generic; |
||
Line 224: | Line 481: | ||
} |
} |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>Total before tax: $22,000,000,000,000,005.72 |
<pre>Total before tax: $22,000,000,000,000,005.72 |
||
Line 233: | Line 490: | ||
{{libheader|clojurewerkz/money}} |
{{libheader|clojurewerkz/money}} |
||
< |
<syntaxhighlight lang="clojure">(require '[clojurewerkz.money.amounts :as ma]) |
||
(require '[clojurewerkz.money.currencies :as mc]) |
(require '[clojurewerkz.money.currencies :as mc]) |
||
(require '[clojurewerkz.money.format :as mf]) |
(require '[clojurewerkz.money.format :as mf]) |
||
Line 243: | Line 500: | ||
(println "Total before tax: " (mf/format pre-tax)) |
(println "Total before tax: " (mf/format pre-tax)) |
||
(println " Tax: " (mf/format tax)) |
(println " Tax: " (mf/format tax)) |
||
(println " Total with tax: " (mf/format (ma/plus pre-tax tax))))</ |
(println " Total with tax: " (mf/format (ma/plus pre-tax tax))))</syntaxhighlight> |
||
{{out}} |
|||
<pre> |
|||
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</pre> |
|||
{{libheader|io.randomseed/bankster}} |
|||
<syntaxhighlight lang="clojure">(require '[io.randomseed.bankster.money :as m]) |
|||
(let [burgers (m/mul #money[USD 5.50] 4000000000000000) |
|||
milkshakes (m/mul #money[USD 2.86] 2) |
|||
pre-tax (m/add burgers milkshakes) |
|||
tax (m/with-rounding UP (m/mul pre-tax 0.0765))] |
|||
(println "Total before tax: " (m/format pre-tax)) |
|||
(println " Tax: " (m/format tax)) |
|||
(println " Total with tax: " (m/format (m/add pre-tax tax))))</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
Line 252: | Line 527: | ||
=={{header|COBOL}}== |
=={{header|COBOL}}== |
||
COBOL supports up to 31 digits of precision |
COBOL supports up to 31 digits of decimal precision, so won't need any fancy currency/BigInteger types! |
||
so won't need any fancy currency/BigInteger types! |
|||
During calculations the default ROUNDED MODE IS clause, when specified, is NEAREST-AWAY-FROM-ZERO. When ROUNDED is not specified, the default mode is TRUNCATION. The term "banker's rounding" implies NEAREST-EVEN. |
|||
{{works with|GNU Cobol|2.1}} |
{{works with|GNU Cobol|2.1}} |
||
< |
<syntaxhighlight lang="cobol"> >>SOURCE FREE |
||
IDENTIFICATION DIVISION. |
IDENTIFICATION DIVISION. |
||
PROGRAM-ID. currency-example. |
PROGRAM-ID. currency-example. |
||
Line 274: | Line 550: | ||
PROCEDURE DIVISION. |
PROCEDURE DIVISION. |
||
COMPUTE total, total-edited = |
COMPUTE total rounded, total-edited rounded = |
||
num-burgers * Burger-Price + num-milkshakes * Milkshake-Price |
num-burgers * Burger-Price + num-milkshakes * Milkshake-Price |
||
DISPLAY "Total before tax: " total-edited |
DISPLAY "Total before tax: " total-edited |
||
COMPUTE tax, tax-edited = total * (Tax-Rate / 100) |
COMPUTE tax rounded, tax-edited rounded = total * (Tax-Rate / 100) |
||
DISPLAY " Tax: " tax-edited |
DISPLAY " Tax: " tax-edited |
||
ADD tax TO total GIVING total-edited |
ADD tax TO total GIVING total-edited rounded |
||
DISPLAY " Total with tax: " total-edited |
DISPLAY " Total with tax: " total-edited |
||
. |
. |
||
END PROGRAM currency-example.</ |
END PROGRAM currency-example.</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 290: | Line 566: | ||
<pre> |
<pre> |
||
Total before tax: $22000000000000005.72 |
Total before tax: $22000000000000005.72 |
||
Tax: $1683000000000000. |
Tax: $1683000000000000.44 |
||
Total with tax: $23683000000000006. |
Total with tax: $23683000000000006.16 |
||
</pre> |
|||
=={{header|Common Lisp}}== |
|||
Let us just use the built-in and convenient rationals (which use two bignums for numerator and denominator). |
|||
<syntaxhighlight lang="lisp">(defun print-$ (rat &key (prefix "") (stream t)) |
|||
(multiple-value-bind (dollars cents) (truncate rat) |
|||
(format stream "~A~D.~D~%" prefix dollars (round (* 100 cents))))) |
|||
(defun compute-check (order-alist tax-rate) |
|||
(let* ((total-before-tax |
|||
(loop :for (amount . price) in order-alist |
|||
:sum (* (rationalize price) amount))) |
|||
(tax (* (rationalize tax-rate) total-before-tax))) |
|||
(print-$ total-before-tax :prefix "Total before tax: ") |
|||
(print-$ tax :prefix "Tax: ") |
|||
(print-$ (+ total-before-tax tax) :prefix "Total with tax: "))) |
|||
(compute-check '((4000000000000000 . 5.5) (2 . 2.86)) 0.0765)</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Total before tax: 22000000000000005.72 |
|||
Tax: 1683000000000000.44 |
|||
Total with tax: 23683000000000006.16</pre> |
|||
A reader macro can be used as extra nice icing on the cake: |
|||
<syntaxhighlight lang="lisp">(defun read-$ (stream char) |
|||
(declare (ignore char)) |
|||
(let* ((str (with-output-to-string (out) |
|||
;; TODO: read integer, if dot, read dot and another integer |
|||
(loop :for next = (peek-char nil stream t nil t) |
|||
:while (or (digit-char-p next) (char= next #\.)) |
|||
:do (write-char (read-char stream t nil t) out)))) |
|||
(dot-pos (position #\. str)) |
|||
(dollars (parse-integer str :end dot-pos)) |
|||
(cents (if dot-pos |
|||
(/ (parse-integer str :start (+ dot-pos 1)) |
|||
(expt 10 (- (length str) (+ dot-pos 1)))) |
|||
0))) |
|||
(+ dollars cents))) |
|||
(set-macro-character #\$ #'read-$ t) |
|||
(defun print-$ (rat &key (prefix "") (stream t)) |
|||
(multiple-value-bind (dollars cents) (truncate rat) |
|||
(format stream "~A~D.~D~%" prefix dollars (round (* 100 cents))))) |
|||
(defun compute-check (order-alist tax-rate) |
|||
(let* ((total-before-tax |
|||
(loop :for (amount . price) in order-alist |
|||
:sum (* price amount))) |
|||
(tax (* (rationalize tax-rate) total-before-tax))) |
|||
(print-$ total-before-tax :prefix "Total before tax: ") |
|||
(print-$ tax :prefix "Tax: ") |
|||
(print-$ (+ total-before-tax tax) :prefix "Total with tax: "))) |
|||
(compute-check '((4000000000000000 . $5.5) (2 . $2.86)) 0.0765)</syntaxhighlight> |
|||
=={{header|Delphi}}== |
|||
{{libheader| System.SysUtils}} |
|||
{{libheader| Velthuis.BigRationals}} |
|||
{{libheader| Velthuis.BigDecimals}} |
|||
{{libheader| Velthuis.BigIntegers}} |
|||
{{Trans|Go}} |
|||
<syntaxhighlight lang="delphi"> |
|||
program Currency; |
|||
{$APPTYPE CONSOLE} |
|||
uses |
|||
System.SysUtils, |
|||
Velthuis.BigRationals, |
|||
Velthuis.BigDecimals, |
|||
Velthuis.BigIntegers; |
|||
var |
|||
one: BigInteger; |
|||
hundred: BigInteger; |
|||
half: BigRational; |
|||
type |
|||
TDc = record |
|||
value: BigInteger; |
|||
function ToString: string; |
|||
function Extend(n: BigInteger): TDc; |
|||
class operator Add(a, b: TDc): TDc; |
|||
end; |
|||
TTR = record |
|||
value: BigRational; |
|||
function SetString(const s: string; var TR: TTR): boolean; |
|||
function Tax(dc: TDc): TDc; |
|||
end; |
|||
{ TDc } |
|||
// Extend returns extended price of a unit price. |
|||
class operator TDc.Add(a, b: TDc): TDc; |
|||
begin |
|||
Result.value := a.value + b.value; |
|||
end; |
|||
function TDc.Extend(n: BigInteger): TDc; |
|||
begin |
|||
Result.value := n * value; |
|||
end; |
|||
function TDc.ToString: string; |
|||
var |
|||
d: BigInteger; |
|||
begin |
|||
d := value.Divide(value, 100); |
|||
if value < 0 then |
|||
value := -value; |
|||
Result := Format('%s.%2s', [d.ToString, (value mod 100).ToString]); |
|||
end; |
|||
// ParseDC parses dollars and cents as a string into a DC. |
|||
function ParseDC(s: string; var Dc: TDc): Boolean; |
|||
var |
|||
r: BigRational; |
|||
d: BigDecimal; |
|||
begin |
|||
Result := d.TryParse(s, d); |
|||
if not Result then |
|||
begin |
|||
Dc.value := 0; |
|||
exit(false); |
|||
end; |
|||
r := r.Create(d); |
|||
r := r.Multiply(r, 100); |
|||
if BigInteger.Compare(r.Denominator, 1) <> 0 then |
|||
begin |
|||
Dc.value := 0; |
|||
exit(false); |
|||
end; |
|||
Result := true; |
|||
Dc.value := r.Numerator; |
|||
end; |
|||
{ TTR } |
|||
function TTR.SetString(const s: string; var TR: TTR): boolean; |
|||
var |
|||
d: BigDecimal; |
|||
begin |
|||
Result := d.TryParse(s, d); |
|||
if Result then |
|||
TR.value := BigRational.Create(d); |
|||
end; |
|||
function TTR.Tax(dc: TDc): TDc; |
|||
var |
|||
r: BigRational; |
|||
i: BigInteger; |
|||
begin |
|||
r := BigRational.Create(dc.value, 1); |
|||
r := r.Multiply(r, self.value); |
|||
r := r.add(r, half); |
|||
i := i.Divide(r.Numerator, r.Denominator); |
|||
Result.value := i; |
|||
end; |
|||
var |
|||
hamburgerPrice, milkshakePrice, totalBeforeTax, tax, total: TDc; |
|||
taxRate: TTR; |
|||
begin |
|||
one := 1; |
|||
hundred := 100; |
|||
half := BigRational.Create(1, 2); |
|||
if not ParseDC('5.50', hamburgerPrice) then |
|||
begin |
|||
Writeln('Invalid hamburger price'); |
|||
halt(1); |
|||
end; |
|||
if not ParseDC('2.86', milkshakePrice) then |
|||
begin |
|||
Writeln('Invalid milkshake price'); |
|||
halt(2); |
|||
end; |
|||
if not taxRate.SetString('0.0765', taxRate) then |
|||
begin |
|||
Writeln('Invalid tax rat'); |
|||
halt(3); |
|||
end; |
|||
totalBeforeTax := hamburgerPrice.Extend(4000000000000000) + milkshakePrice.Extend(2); |
|||
tax := taxRate.Tax(totalBeforeTax); |
|||
total := totalBeforeTax + tax; |
|||
Writeln('Total before tax: ', totalBeforeTax.ToString: 22); |
|||
Writeln(' Tax: ', tax.ToString: 22); |
|||
Writeln(' Total: ', total.ToString: 22); |
|||
readln; |
|||
end.</syntaxhighlight> |
|||
=={{header|F_Sharp|F#}}== |
|||
<syntaxhighlight lang="fsharp">open System |
|||
let hamburgers = 4000000000000000M |
|||
let hamburgerPrice = 5.50M |
|||
let milkshakes = 2M |
|||
let milkshakePrice = 2.86M |
|||
let taxRate = 0.0765M |
|||
let total = hamburgers * hamburgerPrice + milkshakes * milkshakePrice |
|||
let tax = total * taxRate |
|||
let totalWithTax = total + tax |
|||
printfn "Total before tax:\t$%M" <| Math.Round (total, 2) |
|||
printfn " Tax:\t$%M" <| Math.Round (tax, 2) |
|||
printfn " Total:\t$%M" <| Math.Round (totalWithTax, 2)</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Total before tax: $22000000000000005.72 |
|||
Tax: $1683000000000000.44 |
|||
Total with tax: $23683000000000006.16 |
|||
</pre> |
|||
=={{header|Factor}}== |
|||
Factor's <tt>ratio</tt> type can handle arbitrary-precision calculations with rational numbers. The <tt>money</tt> vocabulary implements convenience words for treating rationals as money. The <tt>DECIMAL:</tt> parsing word is used to convert the tax rate <tt>0.0765</tt> to a ratio <tt>153/2000</tt>. The <tt>money.</tt> word is used to print the subtotal <tt>22000000000000005+18/25</tt>, tax <tt>1683000000000000+21879/50000</tt>, and total <tt>23683000000000006+7879/50000</tt> formatted as you would expect. |
|||
<syntaxhighlight lang="factor">USING: combinators.smart io kernel math math.functions money ; |
|||
10 15 ^ 4 * 5+50/100 * ! hamburger subtotal |
|||
2 2+86/100 * ! milkshake subtotal |
|||
+ ! subtotal |
|||
dup DECIMAL: 0.0765 * ! tax |
|||
[ + ] preserving ! total |
|||
"Total before tax: " write [ money. ] 2dip |
|||
"Tax: " write [ money. ] dip |
|||
"Total with tax: " write money.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Total price 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 |
|||
</pre> |
|||
=={{header|FreeBASIC}}== |
|||
Pero el subtotal y el tax los muestra redondeados. Y no encuentro el porqué. |
|||
<syntaxhighlight lang="freebasic">Dim As Longint hamburger_p = 550 |
|||
Dim As Longint hamburger_q = 4000000000000000 |
|||
Dim As Longint hamburger_v = hamburger_p * hamburger_q |
|||
Dim As Longint milkshake_p = 286 |
|||
Dim As Longint milkshake_q = 2 |
|||
Dim As Longint milkshake_v = milkshake_p * milkshake_q |
|||
Dim As Longint subtotal = hamburger_v + milkshake_v |
|||
Dim As Longint tax = subtotal * .765 |
|||
Print Using "\ \ \ \ \ \ \ \";"item";"price";"quantity";"value" |
|||
Print Using "hamburger ##.## ################ #####################.##";hamburger_p/100;hamburger_q;hamburger_v/100 |
|||
Print Using "milkshake ##.## ################ #####################.##";milkshake_p/100;milkshake_q;milkshake_v/100 |
|||
? |
|||
Print Using " subtotal #####################.##";subtotal/10 |
|||
Print Using " tax #####################.##";tax/100 |
|||
Print Using " total #####################.##";subtotal/10+tax/100 |
|||
Sleep</syntaxhighlight> |
|||
=={{header|Frink}}== |
|||
Frink tracks units of measure through all calculations, so you can specify quantities as "dollar", "cent", etc. |
|||
<syntaxhighlight lang="frink"> |
|||
st = 4000000000000000 * 5.50 dollars + 2 * 2.86 dollars |
|||
tax = round[st * 7.65 percent, cent] |
|||
total = st + tax |
|||
println["Subtotal: " + format[st, "dollars", 2]] |
|||
println["Tax: " + format[tax, "dollars", 2]] |
|||
println["Total: " + format[total, "dollars", 2]] |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Subtotal: 22000000000000005.72 dollars |
|||
Tax: 1683000000000000.44 dollars |
|||
Total: 23683000000000006.16 dollars |
|||
</pre> |
|||
=={{header|FutureBasic}}== |
|||
<syntaxhighlight lang="futurebasic"> |
|||
local fn Lunch_Invoice( burger_price as CFStringRef, burger_amount as CFStringRef, shake_price as CFStringRef, shake_amount as CFStringRef, tax as CFStringRef ) |
|||
'~'1 |
|||
DecimalNumberRef burgerPriceDecimal = fn DecimalNumberWithString( burger_price ) |
|||
DecimalNumberRef burgerAmountDecimal = fn DecimalNumberWithString( burger_amount ) |
|||
DecimalNumberRef burgersDecimal = fn DecimalNumberByMultiplyingBy( burgerPriceDecimal, burgerAmountDecimal ) |
|||
DecimalNumberRef shakePriceDecimal = fn DecimalNumberWithString( shake_price ) |
|||
DecimalNumberRef shakeAmountDecimal = fn DecimalNumberWithString( shake_amount ) |
|||
DecimalNumberRef shakesDecimal = fn DecimalNumberByMultiplyingBy( shakePriceDecimal, shakeAmountDecimal ) |
|||
DecimalNumberRef taxDecimal = fn DecimalNumberWithString( tax ) |
|||
DecimalNumberRef subtotalDecimal = fn DecimalNumberByAdding( burgersDecimal, shakesDecimal ) |
|||
DecimalNumberRef taxTotalDecimal = fn DecimalNumberByMultiplyingBy( subtotalDecimal, taxDecimal ) |
|||
DecimalNumberRef adjTaxTotalDecimal = fn DecimalNumberByAdding( taxTotalDecimal, fn DecimalNumberWithString( @"0.01" ) ) |
|||
DecimalNumberRef billTotalDecimal = fn DecimalNumberByAdding( subtotalDecimal, adjTaxTotalDecimal ) |
|||
CFStringRef burgersString = fn DecimalNumberString( burgersDecimal ) |
|||
CFStringRef shakesString = fn DecimalNumberString( shakesDecimal ) |
|||
CFStringRef taxTotalString = fn DecimalNumberString( adjTaxTotalDecimal ) |
|||
CFStringRef billTotalString = fn DecimalNumberString( billTotalDecimal ) |
|||
printf @"%@", fn StringByPaddingToLength( @"", 55, @"-", 0 ) |
|||
printf @"Item Price Quantity Cost" |
|||
printf @"Hamburgers %6s %18s %18s", fn StringUTF8String( burger_price ), fn StringUTF8String( burger_amount ), fn StringUTF8String( burgersString ) |
|||
printf @"Milkshakes %6s %18s %18s", fn StringUTF8String( shake_price ), fn StringUTF8String( shake_amount ), fn StringUTF8String( shakesString ) |
|||
printf @"%@", fn StringByPaddingToLength( @"", 55, @"-", 0 ) |
|||
printf @"%34s %@", fn StringUTF8String( @"Subtotal:" ), fn DecimalNumberString( subtotalDecimal ) |
|||
printf @"%35s %@", fn StringUTF8String( @" Tax: " ), fn StringSubstringToIndex( taxTotalString, len(taxTotalString) - 3 ) |
|||
printf @"%34s %@", fn StringUTF8String( @" Total:" ), fn StringSubstringToIndex( billTotalString, len(billTotalString) - 3 ) |
|||
end fn |
|||
NSLog( @"%@", fn WindowPrintViewString( 1 ) ) |
|||
HandleEvents |
|||
</syntaxhighlight> |
|||
{{output}} |
|||
<pre> |
|||
------------------------------------------------------- |
|||
Item Price Quantity Cost |
|||
Hamburgers 5.50 4000000000000000 22000000000000000 |
|||
Milkshakes 2.86 2 5.72 |
|||
------------------------------------------------------- |
|||
Subtotal: 22000000000000005.72 |
|||
Tax: 1683000000000000.44 |
|||
Total: 23683000000000006.16 |
|||
</pre> |
</pre> |
||
=={{header|Go}}== |
=={{header|Go}}== |
||
< |
<syntaxhighlight lang="go">package main |
||
import ( |
import ( |
||
Line 382: | Line 988: | ||
fmt.Printf(" Tax: %22s\n", tax) |
fmt.Printf(" Tax: %22s\n", tax) |
||
fmt.Printf(" Total: %22s\n", total) |
fmt.Printf(" Total: %22s\n", total) |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 389: | Line 995: | ||
Total: 23683000000000006.16 |
Total: 23683000000000006.16 |
||
</pre> |
</pre> |
||
=={{header|Haskell}}== |
=={{header|Haskell}}== |
||
< |
<syntaxhighlight lang="haskell">import Data.Fixed |
||
import Text.Printf |
import Text.Printf |
||
Line 409: | Line 1,016: | ||
printAmount "Subtotal" subtotal |
printAmount "Subtotal" subtotal |
||
printAmount "Tax" tx |
printAmount "Tax" tx |
||
printAmount "Total" total</ |
printAmount "Total" total</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 423: | Line 1,030: | ||
We use a naive implementation with arbitrary precision (rational) numbers: |
We use a naive implementation with arbitrary precision (rational) numbers: |
||
< |
<syntaxhighlight lang="j">require 'format/printf' |
||
fmtD=: 0j2&": NB. format rational as decimal |
|||
Items=: ;: 'Hamburger Milkshake' |
|||
Quantities=: 4000000000000000 2 |
|||
Items=: ;: 'Hamburger Milkshake' |
|||
Prices=: x: 5.50 2.86 |
|||
Prices=: x: 5.50 2.86 |
|||
Tax_rate=: x: 0.0765 |
Tax_rate=: x: 0.0765 |
||
Values=: Quantities * Prices |
|||
Subtotal=: +/ Values |
|||
Tax=: Tax_rate * Subtotal |
|||
Total=: Subtotal + Tax |
|||
makeBill=: verb define |
|||
'items prices quantities'=. y |
|||
Item Price Quantity Value |
|||
values=. prices * quantities |
|||
%9s %8s %20d %22s |
|||
subtotal=. +/ values |
|||
%9s %8s %20d %22s |
|||
tax=. Tax_rate * subtotal |
|||
------------------------------- |
|||
total=. subtotal + tax |
|||
Subtotal: %20s |
|||
Tax: %20s |
|||
'%9s %8s %20s %22s' printf ;:'Item Price Quantity Value' |
|||
Total: %20s |
|||
'%9s %8.2f %20d %22.2f' printf"1 items ,. <"0 prices ,. quantities ,. values |
|||
'%62s' printf <'-------------------------------' |
|||
'%40s %21.2f' printf"1 (;:'Subtotal: Tax: Total:') ,. subtotal;tax;total |
|||
) |
) |
||
makeBill Items;Prices;Quantities</syntaxhighlight> |
|||
Vals=: (,Items ,. (fmtD&.> Prices) ,. (<"0 Quantities) ,. (fmtD&.> Values)) , fmtD&.> Subtotal,Tax,Total |
|||
OutputTemplate printf Vals</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> Item Price Quantity Value |
||
Hamburger 5.50 4000000000000000 22000000000000000.00 |
Hamburger 5.50 4000000000000000 22000000000000000.00 |
||
Milkshake 2.86 2 5.72 |
Milkshake 2.86 2 5.72 |
||
Line 459: | Line 1,063: | ||
=={{header|Java}}== |
=={{header|Java}}== |
||
< |
<syntaxhighlight lang="java">import java.math.*; |
||
import java.util.*; |
import java.util.*; |
||
Line 497: | Line 1,101: | ||
System.out.printf(" Total: %20.2f%n", subtotal.add(tax)); |
System.out.printf(" Total: %20.2f%n", subtotal.add(tax)); |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
<pre>Subtotal: 22000000000000005.72 |
<pre>Subtotal: 22000000000000005.72 |
||
Line 504: | Line 1,108: | ||
=={{header|JavaScript}}== |
=={{header|JavaScript}}== |
||
< |
<syntaxhighlight lang="javascript">const money = require('money-math') |
||
let hamburgers = 4000000000000000 |
let hamburgers = 4000000000000000 |
||
Line 528: | Line 1,132: | ||
console.log('Tax:', taxTotal) |
console.log('Tax:', taxTotal) |
||
console.log('Total:', total) |
console.log('Total:', total) |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|jq}}== |
|||
For simplicity, we will use the gojq implementation of jq |
|||
as it supports unbounded-precision integer arithmetic. |
|||
If the standard implementation of jq were used, |
|||
then it would probably be simplest to use the BigInt.jq library, |
|||
which uses strings for precision. |
|||
The strategy here is to use cents except when presenting |
|||
results as dollars and cents, and when calculating the taxes. |
|||
<syntaxhighlight lang="jq">def lpad($len): tostring | ($len - length) as $l | (" " * $l)[:$l] + .; |
|||
# print as dollars and cents |
|||
def dollars: |
|||
(. % 100) as $c |
|||
| "$\((. - $c) /100).\($c)"; |
|||
def dollars($width): |
|||
dollars | lpad($width); |
|||
def innerproduct($y): |
|||
. as $x |
|||
| reduce range(0;$x|length) as $i (0; . + ($x[$i]*$y[$i])); |
|||
def plus($y): |
|||
. as $x |
|||
| reduce range(0;$x|length) as $i ([]; .[$i] = ($x[$i]+$y[$i])); |
|||
# Round up or down |
|||
def integer_division($y): |
|||
(. % $y) as $remainder |
|||
| (. - $remainder) / $y |
|||
| if $remainder * 2 > $y then . + 1 else . end; |
|||
# For computing taxes |
|||
def precision: 10000; |
|||
def cents: integer_division(precision); |
|||
### The task: |
|||
def p: [550, 286]; |
|||
def q: [4000000000000000, 2]; |
|||
def taxrate: 765; # relative to `precision` |
|||
(p | innerproduct(q)) as $before_tax # cents |
|||
| ($before_tax * taxrate) as $taxes # relative to precision |
|||
| ((($before_tax * precision) + $taxes) | cents) as $after_tax # cents |
|||
| ($after_tax|tostring|length + 2) as $width |
|||
| |
|||
" Total before tax: \($before_tax | dollars($width))", |
|||
" - tax: \($taxes | cents | dollars($width))", |
|||
" Total after tax: \($after_tax | dollars($width))"</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Total before tax: $22000000000000005.72 |
|||
- tax: $1683000000000000.44 |
|||
Total after tax: $23683000000000006.16 |
|||
</pre> |
|||
=={{header|Julia}}== |
|||
{{works with|Julia|1.2}} |
|||
<syntaxhighlight lang="julia">using Printf |
|||
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</syntaxhighlight> |
|||
{{out}} |
|||
<pre> - tot. before tax: 22000000000000005.72 $ |
|||
- tax: 1683000000000000.44 $ |
|||
- tot. after tax: 23683000000000006.16 $</pre> |
|||
=={{header|Kotlin}}== |
=={{header|Kotlin}}== |
||
< |
<syntaxhighlight lang="scala">// version 1.1.2 |
||
import java.math.BigDecimal |
import java.math.BigDecimal |
||
Line 549: | Line 1,237: | ||
println("Tax thereon @ 7.65% : ${fmt.format(tax)}") |
println("Tax thereon @ 7.65% : ${fmt.format(tax)}") |
||
println("Total price after tax : ${fmt.format(price + tax)}") |
println("Total price after tax : ${fmt.format(price + tax)}") |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 558: | Line 1,246: | ||
</pre> |
</pre> |
||
=={{header| |
=={{header|Lua}}== |
||
Using the lbc library for arbitrary precision support. |
|||
{{works with|Rakudo|2016.01}} |
|||
<syntaxhighlight lang="lua">C = setmetatable(require("bc"), {__call=function(t,...) return t.new(...) end}) |
|||
No need for a special type in Perl 6, since the <tt>Rat</tt> type is used for normal fractions. |
|||
C.digits(6) -- enough for .nn * .nnnn ==> .nnnnnn, follow with trunc(2) to trim trailing zeroes |
|||
(In order to achieve imprecision, you have to explicitly use scientific notation, |
|||
or use the <tt>Num</tt> type, or calculate a result that requires a denominator in excess of <tt>2 ** 64</tt>. (There's no limit on the numerator.)) |
|||
<lang perl6>my @check = q:to/END/.lines.map: { [.split(/\s+/)] }; |
|||
Hamburger 5.50 4000000000000000 |
|||
Milkshake 2.86 2 |
|||
END |
|||
subtot = (C"4000000000000000" * C"5.50" + C"2" * C"2.86"):trunc(2) -- cosmetic trunc |
|||
my $tax-rate = 0.0765; |
|||
tax = (subtot * C"0.0765" + C"0.005"):trunc(2) -- rounding trunc |
|||
total = (subtot + tax):trunc(2) -- cosmetic trunc |
|||
print(("Before tax: %20s"):format(subtot:tostring())) |
|||
my $fmt = "%-10s %8s %18s %22s\n"; |
|||
print(("Tax : %20s"):format(tax:tostring())) |
|||
print(("With tax : %20s"):format(total:tostring()))</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Before tax: 22000000000000005.72 |
|||
Tax : 1683000000000000.44 |
|||
With tax : 23683000000000006.16</pre> |
|||
=={{header|M2000 Interpreter}}== |
|||
This task written in M2000 Environment running on Wine 3.6, in a Linux Ubuntu Studio. M2000 environment is an ActiveX object, written with VB6, which use many types from [https://en.wikipedia.org/wiki/Variant_type COM Variant Type]. |
|||
<syntaxhighlight lang="m2000 interpreter"> |
|||
Module Currency_Task { |
|||
Locale 1033 |
|||
Font "Courier New" |
|||
Form 80,32 |
|||
\\Decimal type |
|||
hamburgers=4000000000000000@ |
|||
\\ Currency type |
|||
hamburger_price=5.5# |
|||
milkshakes=2# |
|||
milkshake_price=2.86# |
|||
tax_rate=0.0765# |
|||
\\ Using Columns with variable width in console |
|||
PrHeadLine("Item","price","quantity", "value") |
|||
PrLine("hamburger",hamburger_price,hamburgers,hamburgers*hamburger_price) |
|||
PrLine("milkshake", milkshake_price,milkshakes,milkshakes*milkshake_price) |
|||
PrResults( "subtotal", hamburgers*hamburger_price+milkshakes*milkshake_price) |
|||
PrResults("tax", (hamburgers*hamburger_price+milkshakes*milkshake_price)*tax_rate) |
|||
\\ 1 is double by default we can use 1# or 1@ |
|||
PrResults("total", (hamburgers*hamburger_price+milkshakes*milkshake_price)*(tax_rate+1)) |
|||
\\ Using variables for partial calculations. They get type from expression result |
|||
h_p_q=hamburgers*hamburger_price |
|||
m_p_q=milkshakes*milkshake_price |
|||
\\ Using format$ to prepare final strings |
|||
Print format$("{0:15}{1:-8}{2:-25}{3:-25}","Item", "price", "quantity", "value") |
|||
Print format$("{0:15}{1:2:-8}{2:0:-25}{3:2:-25}","hamburger",hamburger_price,hamburgers, h_p_q) |
|||
Print format$("{0:15}{1:2:-8}{2:0:-25}{3:2:-25}","milkshake", milkshake_price,milkshakes,m_p_q) |
|||
Print format$("{0:-48}{1:2:-25}","subtotal", h_p_q+m_p_q) |
|||
Print format$("{0:-48}{1:2:-25}","tax", (h_p_q+m_p_q)*tax_rate) |
|||
Print format$("{0:-48}{1:2:-25}","total", (h_p_q+m_p_q)*(tax_rate+1)) |
|||
\\ Another time to feed Document to export to clipboard |
|||
Document Doc$=format$("{0:15}{1:-8}{2:-25}{3:-25}","Item", "price", "quantity", "value")+{ |
|||
}+format$("{0:15}{1:2:-8}{2:0:-25}{3:2:-25}","hamburger",hamburger_price,hamburgers, h_p_q)+{ |
|||
}+format$("{0:15}{1:2:-8}{2:0:-25}{3:2:-25}","milkshake", milkshake_price,milkshakes,m_p_q)+{ |
|||
}+format$("{0:-48}{1:2:-25}","subtotal", h_p_q+m_p_q)+{ |
|||
}+format$("{0:-48}{1:2:-25}","tax", (h_p_q+m_p_q)*tax_rate)+{ |
|||
}+format$("{0:-48}{1:2:-25}","total", (h_p_q+m_p_q)*(tax_rate+1))+{ |
|||
} |
|||
clipboard Doc$ |
|||
\\ one line user function definition |
|||
\\ x get type from passed value |
|||
Def ExpressionType$(x)=Type$(X) |
|||
\\ Check Expression final type |
|||
Print ExpressionType$(hamburgers)="Decimal" |
|||
Print ExpressionType$(milkshakes)="Currency" |
|||
Print ExpressionType$(h_p_q)="Decimal" |
|||
Print ExpressionType$(m_p_q)="Currency" |
|||
Print ExpressionType$((h_p_q+m_p_q)*tax_rate)="Decimal" |
|||
Print ExpressionType$((h_p_q+m_p_q)*(tax_rate+1))="Decimal" |
|||
Sub PrHeadLine(a$,b$,c$,d$) |
|||
Print Part $(1,15),a$,$(3,8),b$, $(3,25),c$, $(3,25),d$ |
|||
Print |
|||
End Sub |
|||
Sub PrLine(a$,b,c,d) |
|||
Print Part $(1,15),a$,$("0.00"),$(3,8),b, $("0"),$(3,25),c,$("0.00"), $(3,25),d |
|||
Print |
|||
End Sub |
|||
Sub PrResults(a$,b) |
|||
Print Part $(3,48),a$,$("0.00"),$(3,25),b |
|||
Print |
|||
End Sub |
|||
} |
|||
Currency_Task |
|||
</syntaxhighlight> |
|||
Optional with $ and thousands separator (a smaller version from above) |
|||
<syntaxhighlight lang="m2000 interpreter"> |
|||
Module Currency_Task { |
|||
Locale 1033 |
|||
Font "Courier New" |
|||
Form 80,32 |
|||
hamburgers=4000000000000000@ |
|||
hamburger_price=5.5# |
|||
milkshakes=2# |
|||
milkshake_price=2.86# |
|||
tax_rate=0.0765# |
|||
PrHeadLine("Item","price","quantity", "value") |
|||
PrLine("hamburger",hamburger_price,hamburgers,hamburgers*hamburger_price) |
|||
PrLine("milkshake", milkshake_price,milkshakes,milkshakes*milkshake_price) |
|||
PrResults( "subtotal", hamburgers*hamburger_price+milkshakes*milkshake_price) |
|||
PrResults("tax", (hamburgers*hamburger_price+milkshakes*milkshake_price)*tax_rate) |
|||
PrResults("total", (hamburgers*hamburger_price+milkshakes*milkshake_price)*(tax_rate+1)) |
|||
h_p_q=hamburgers*hamburger_price |
|||
m_p_q=milkshakes*milkshake_price |
|||
Document Doc$=format$("{0:15}{1:-8}{2:-25}{3:-30}","Item", "price", "quantity", "value")+{ |
|||
}+format$("{0:15}{1:-8}{2:-25}{3:-30}","hamburger",str$(hamburger_price,"$#,##0.00"),str$(hamburgers, "#,##0"), Str$(h_p_q,"$#,##0.00"))+{ |
|||
}+format$("{0:15}{1:-8}{2:-25}{3:-30}","milkshake", str$(milkshake_price,"$#,##0.00"),Str$(milkshakes, "#,##0"), Str$(m_p_q,"$#,##0.00"))+{ |
|||
}+format$("{0:-48}{1:-30}","subtotal", Str$(h_p_q+m_p_q,"$#,##0.00"))+{ |
|||
}+format$("{0:-48}{1:-30}","tax", Str$((h_p_q+m_p_q)*tax_rate,"$#,##0.00"))+{ |
|||
}+format$("{0:-48}{1:-30}","total", Str$((h_p_q+m_p_q)*(tax_rate+1),"$#,##0.00"))+{ |
|||
} |
|||
clipboard Doc$ |
|||
Sub PrHeadLine(a$,b$,c$,d$) |
|||
Print Part $(1,15),a$,$(3,8),b$, $(3,25),c$, $(3,30),d$ |
|||
Print |
|||
End Sub |
|||
Sub PrLine(a$,b,c,d) |
|||
Print Part $(1,15),a$,$("$#,###.00"),$(3,8),b, $("#,##0"),$(3,25),c,$("$#,###.00"), $(3,30),d |
|||
Print |
|||
End Sub |
|||
Sub PrResults(a$,b) |
|||
Print Part $(3,48),a$,$("$#,###.00"),$(3,30),b |
|||
Print |
|||
End Sub |
|||
} |
|||
Currency_Task |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="height:30ex;overflow:scroll"> |
|||
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 |
|||
From Optional |
|||
Item price quantity value |
|||
hamburger $5.50 4,000,000,000,000,000 $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 |
|||
</pre > |
|||
=={{header|Maple}}== |
|||
<syntaxhighlight lang="maple"> |
|||
Digits := 50; |
|||
tax := .0765; |
|||
burgersquantity := 4000000000000000; |
|||
burgersprice := 5.50; |
|||
burgerscost := burgersquantity * burgersprice; |
|||
milkshakesquantity := 2; |
|||
milkshakesprice := 2.86; |
|||
milkshakescost := milkshakesquantity * milkshakesprice; |
|||
total := burgerscost + milkshakescost; |
|||
printf("%.2f\n",total); |
|||
totaltax := total * tax; |
|||
printf("%.2f\n",totaltax); |
|||
totalprice := totaltax + total; |
|||
printf("%.2f\n",totalprice); |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
22000000000000005.72 |
|||
1683000000000000.44 |
|||
23683000000000006.16 |
|||
</pre> |
|||
=={{header|Mathematica}} / {{header|Wolfram Language}}== |
|||
<syntaxhighlight lang="mathematica">total = 4000000000000000 Rationalize[5.50] + 2 Rationalize[2.86]; |
|||
AccountingForm[N[total, 20], {\[Infinity], 2}] |
|||
tax = total Rationalize[0.0765]; |
|||
AccountingForm[N[tax, 20], {\[Infinity], 2}] |
|||
AccountingForm[N[total + tax, 20], {\[Infinity], 2}]</syntaxhighlight> |
|||
{{out}} |
|||
<pre>22000000000000005.72 |
|||
1683000000000000.44 |
|||
23683000000000006.16</pre> |
|||
=={{header|Nim}}== |
|||
{{libheader|bignum}} |
|||
Nim doesn’t provide a standard module to deal with decimal values. There exist some third party modules but we have chosen to use the “bignum” library which provides big integers and big rationals. |
|||
<syntaxhighlight lang="nim">import strutils |
|||
import bignum |
|||
type Currency = Int |
|||
#--------------------------------------------------------------------------------------------------- |
|||
func currency(units, subunits: int): Currency = |
|||
## Build a currency from units and subunits. |
|||
## Units may be negative. Subunits must be in range 0..99. |
|||
if subunits notin 0..99: |
|||
raise newException(ValueError, "wrong value for subunits") |
|||
result = if units >= 0: newInt(units * 100 + subunits) |
|||
else: newInt(subunits * 100 - subunits) |
|||
#--------------------------------------------------------------------------------------------------- |
|||
func currency(value: string): Currency = |
|||
## Build a currency from a string. |
|||
## Negative values are allowed. At most two digits are allowed for subunits. |
|||
const StartingChars = Digits + {'-'} |
|||
if value.len == 0 or value[0] notin StartingChars: |
|||
raise newException(ValueError, "wrong currency string") |
|||
# process sign and units. |
|||
var units = newInt(0) |
|||
var subunits = 0 |
|||
let sign = if value[0] == '-': -1 else: 1 |
|||
var idx = if sign == 1: 0 else: 1 |
|||
while idx < value.len: |
|||
if value[idx] notin Digits: break |
|||
units = 10 * units + ord(value[idx]) - ord('0') |
|||
inc idx |
|||
# Process separator. |
|||
if idx <= value.high: |
|||
if value[idx] != '.': |
|||
raise newException(ValueError, "expected a separator") |
|||
inc idx |
|||
# Process subunits. |
|||
for _ in 0..1: |
|||
let c = if idx >= value.len: '0' else: value[idx] |
|||
if c notin Digits: |
|||
raise newException(ValueError, "wrong value for subunits") |
|||
subunits = 10 * subunits + ord(c) - ord('0') |
|||
inc idx |
|||
if idx <= value.high: |
|||
raise newException(ValueError, "extra characters after subunits digits") |
|||
result = sign * (units * 100 + subunits) |
|||
#--------------------------------------------------------------------------------------------------- |
|||
func `//`(a, b: int): Rat = |
|||
## Create a rational value. |
|||
newRat(a, b) |
|||
#--------------------------------------------------------------------------------------------------- |
|||
func percentage(a: Currency; p: Rat): Currency = |
|||
## Compute a percentage on currency value "a". |
|||
## Returned value is rounded to nearest integer. |
|||
(a * p.num * 10 div p.denom + 5) div 10 |
|||
#--------------------------------------------------------------------------------------------------- |
|||
func `$`(a: Currency): string = |
|||
## Build a string representation of a currency value. |
|||
result = bignum.`$`(a div 100) & '.' & ($(a mod 100).toInt).align(2, '0') |
|||
#——————————————————————————————————————————————————————————————————————————————————————————————————— |
|||
let hamburgers = currency(5, 50) * int 4_000_000_000_000_000 |
|||
let milkshakes = currency("2.86") * 2 |
|||
let rate = 765 // 10_000 |
|||
let beforeTax = hamburgers + milkshakes |
|||
let tax = beforeTax.percentage(rate) |
|||
let total = beforeTax + tax |
|||
# Find the maximum length of numerical value representations. |
|||
let beforeTaxStr = $beforeTax |
|||
let taxStr = $tax |
|||
let totalStr = $total |
|||
let length = max([beforeTaxStr.len, taxStr.len, totalStr.len]) |
|||
# Display the results. |
|||
echo "Total price before tax: ", beforeTaxStr.align(length) |
|||
echo "Tax: ", taxStr.align(length) |
|||
echo "Total with tax: ", totalStr.align(length)</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Total price before tax: 22000000000000005.72 |
|||
Tax: 1683000000000000.44 |
|||
Total with tax: 23683000000000006.16</pre> |
|||
=={{header|OCaml}}== |
|||
Using the [https://ocaml.org/p/decimal/0.3.0 decimal] library. |
|||
<syntaxhighlight lang="ocaml"> |
|||
let () = |
|||
let open Decimal in (* bring all functions and operators into scope locally *) |
|||
let s = of_string in |
|||
let i = of_int in |
|||
let hamburgers = s "4e15" * s "5.50" in |
|||
let milkshakes = i 2 * s "2.86" in |
|||
let tax_rate = s "7.65e-2" in |
|||
let subtotal = hamburgers + milkshakes in |
|||
let tax = subtotal * tax_rate in |
|||
let total = subtotal + tax in |
|||
Printf.printf |
|||
"Subtotal: %20s |
|||
Tax: %20s |
|||
Total: %20s\n" |
|||
(to_string (round ~n:2 subtotal)) |
|||
(to_string (round ~n:2 tax)) |
|||
(to_string (round ~n:2 total)) |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Subtotal: 22000000000000005.72 |
|||
Tax: 1683000000000000.44 |
|||
Total: 23683000000000006.16 |
|||
</pre> |
|||
=={{header|Perl}}== |
|||
<syntaxhighlight lang="perl">use Math::Decimal qw(dec_canonise dec_add dec_mul dec_rndiv_and_rem); |
|||
@check = ( |
|||
[<Hamburger 5.50 4000000000000000>], |
|||
[<Milkshake 2.86 2>] |
|||
); |
|||
my $fmt = "%-10s %8s %18s %22s\n"; |
|||
printf $fmt, <Item Price Quantity Extension>; |
printf $fmt, <Item Price Quantity Extension>; |
||
my $subtotal = |
my $subtotal = dec_canonise(0); |
||
for $line (@check) { |
|||
my $extension = $price * $quant; |
|||
($item,$price,$quant) = @$line; |
|||
$dp = dec_canonise($price); $dq = dec_canonise($quant); |
|||
$extension; |
|||
my $extension = dec_mul($dp,$dq); |
|||
$subtotal = dec_add($subtotal, $extension); |
|||
printf $fmt, $item, $price, $quant, rnd($extension); |
|||
} |
} |
||
my $rate = dec_canonise(0.0765); |
|||
printf $fmt, '', '', '', '-----------------'; |
|||
my $tax = dec_mul($subtotal,$rate); |
|||
printf $fmt, '', '', 'Subtotal ', $subtotal; |
|||
my $total = dec_add($subtotal,$tax); |
|||
printf $fmt, '', '', '', '-----------------'; |
|||
my $tax = ($subtotal * $tax-rate).round(0.01); |
|||
printf $fmt, '', '', ' |
printf $fmt, '', '', 'Subtotal ', rnd($subtotal); |
||
printf $fmt, '', '', 'Tax ', rnd($tax); |
|||
printf $fmt, '', '', 'Total ', rnd($total); |
|||
sub rnd { |
|||
my $total = $subtotal + $tax; |
|||
($q, $r) = dec_rndiv_and_rem("FLR", @_[0], 1); |
|||
printf $fmt, '', '', 'Total ', $total; |
|||
$q . substr((sprintf "%.2f", $r), 1, 3); |
|||
}</syntaxhighlight> |
|||
# make up for lack of a Rat fixed-point printf format |
|||
sub fix2($x) { ($x + 0.001).subst(/ <?after \.\d\d> .* $ /, '') }</lang> |
|||
{{out}} |
{{out}} |
||
<pre>Item Price Quantity Extension |
<pre>Item Price Quantity Extension |
||
Line 599: | Line 1,614: | ||
Tax 1683000000000000.44 |
Tax 1683000000000000.44 |
||
Total 23683000000000006.16</pre> |
Total 23683000000000006.16</pre> |
||
=={{header|Phix}}== |
|||
{{libheader|Phix/mpfr}} |
|||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<span style="color: #7060A8;">requires</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"1.0.4"</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- (mpfr_get_fixed() busted in 1.0.2|3)</span> |
|||
<span style="color: #008080;">include</span> <span style="color: #004080;">mpfr</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span> |
|||
<span style="color: #7060A8;">mpfr_set_default_precision</span><span style="color: #0000FF;">(-</span><span style="color: #000000;">20</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- ensure accuracy to at least 20 d.p.</span> |
|||
<span style="color: #004080;">mpfr</span> <span style="color: #000000;">total_price</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">mpfr_init</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"4000000000000000"</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #000000;">tmp</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">mpfr_init</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"5.5"</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #000000;">tax</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">mpfr_init</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"0.0765"</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #000000;">total</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">mpfr_init</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #7060A8;">mpfr_mul</span><span style="color: #0000FF;">(</span><span style="color: #000000;">total_price</span><span style="color: #0000FF;">,</span><span style="color: #000000;">total_price</span><span style="color: #0000FF;">,</span><span style="color: #000000;">tmp</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">mpfr_set_str</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tmp</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"2.86"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">mpfr_mul_si</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tmp</span><span style="color: #0000FF;">,</span><span style="color: #000000;">tmp</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">mpfr_add</span><span style="color: #0000FF;">(</span><span style="color: #000000;">total_price</span><span style="color: #0000FF;">,</span><span style="color: #000000;">total_price</span><span style="color: #0000FF;">,</span><span style="color: #000000;">tmp</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">mpfr_mul</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tax</span><span style="color: #0000FF;">,</span><span style="color: #000000;">total_price</span><span style="color: #0000FF;">,</span><span style="color: #000000;">tax</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">mpfr_add</span><span style="color: #0000FF;">(</span><span style="color: #000000;">total</span><span style="color: #0000FF;">,</span><span style="color: #000000;">total_price</span><span style="color: #0000FF;">,</span><span style="color: #000000;">tax</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Total before tax:%26s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">mpfr_get_fixed</span><span style="color: #0000FF;">(</span><span style="color: #000000;">total_price</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">comma_fill</span><span style="color: #0000FF;">:=</span><span style="color: #004600;">true</span><span style="color: #0000FF;">)})</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">" Tax:%26s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">mpfr_get_fixed</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tax</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">comma_fill</span><span style="color: #0000FF;">:=</span><span style="color: #004600;">true</span><span style="color: #0000FF;">)})</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">" Total:%26s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">mpfr_get_fixed</span><span style="color: #0000FF;">(</span><span style="color: #000000;">total</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">comma_fill</span><span style="color: #0000FF;">:=</span><span style="color: #004600;">true</span><span style="color: #0000FF;">)})</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{out}} |
|||
<pre> |
|||
Total before tax: 22,000,000,000,000,005.72 |
|||
Tax: 1,683,000,000,000,000.44 |
|||
Total: 23,683,000,000,000,006.16 |
|||
</pre> |
|||
=== 64 bit === |
|||
As it happens, 64-bit Phix uses 80-bit floats for atoms, which actually have just enough accuracy for this task: |
|||
<!--<syntaxhighlight lang="phix">--> |
|||
<span style="color: #7060A8;">requires</span><span style="color: #0000FF;">(</span><span style="color: #000000;">64</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">total_price</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">4000000000000000</span><span style="color: #0000FF;">*</span><span style="color: #000000;">5.5</span><span style="color: #0000FF;">+</span><span style="color: #000000;">2.86</span><span style="color: #0000FF;">*</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span> |
|||
<span style="color: #000000;">tax</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">total_price</span><span style="color: #0000FF;">*</span><span style="color: #000000;">0.0765</span><span style="color: #0000FF;">,</span> |
|||
<span style="color: #000000;">total</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">total_price</span><span style="color: #0000FF;">+</span><span style="color: #000000;">tax</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Total before tax:%,26.2f\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">total_price</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">" Tax:%,26.2f\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">tax</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">" Total:%,26.2f\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">total</span><span style="color: #0000FF;">})</span> |
|||
<!--</syntaxhighlight>--> |
|||
with the same output, however (after deleting the first line) under 32-bit (and under p2js) they would incorrectly end in 4.00/0.25/4.00. |
|||
=={{header|PicoLisp}}== |
|||
<syntaxhighlight lang="text">(scl 2) |
|||
(let |
|||
(Before |
|||
(+ |
|||
(* 4000000000000000 5.50) |
|||
(* 2 2.86) ) |
|||
Tax (*/ Before 7.65 100.00) |
|||
Total (+ Before Tax) |
|||
Fmt (17 27) ) |
|||
(tab Fmt "Total before tax:" (format Before *Scl "." ",")) |
|||
(tab Fmt "Tax:" (format Tax *Scl "." ",")) |
|||
(tab Fmt "Total:" (format Total *Scl "." ",")) )</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Total before tax: 22,000,000,000,000,005.72 |
|||
Tax: 1,683,000,000,000,000.44 |
|||
Total: 23,683,000,000,000,006.16 |
|||
</pre> |
|||
=={{header|Python}}== |
=={{header|Python}}== |
||
This uses Pythons decimal module, |
This uses Pythons decimal module, |
||
(and some copying of names from the |
(and some copying of names from the Raku example). |
||
< |
<syntaxhighlight lang="python">from decimal import Decimal as D |
||
from collections import namedtuple |
from collections import namedtuple |
||
Line 629: | Line 1,704: | ||
total = total_before_tax + tax |
total = total_before_tax + tax |
||
print(fmt % ('', '', '', '--------------------')) |
print(fmt % ('', '', '', '--------------------')) |
||
print(fmt % ('', '', 'Total', total))</ |
print(fmt % ('', '', 'Total', total))</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 640: | Line 1,715: | ||
-------------------- |
-------------------- |
||
Total 23683000000000006.16</pre> |
Total 23683000000000006.16</pre> |
||
=={{header|Quackery}}== |
|||
<syntaxhighlight lang="Quackery"> [ $ "bigrat.qky" loadfile ] now! |
|||
[ 100 * n->v ] is dollars ( n --> n/d ) |
|||
[ n->v v+ ] is cents ( n/d n --> n/d ) |
|||
[ rot n->v v* ] is cost ( n n/d --> n/d ) |
|||
[ $->v drop v* 100 n->v v/ ] is tax ( n/d $ --> n/d ) |
|||
[ 100 n->v v/ |
|||
2 point$ |
|||
$ " $" swap join |
|||
' [ 2 split nip ] ]do[ |
|||
dup -3 peek |
|||
char . = if done |
|||
dup -2 peek |
|||
char . = iff |
|||
[ char 0 join ] |
|||
done |
|||
$ ".00" join ] is currency$ ( n/d --> $ ) |
|||
[ currency$ echo$ ] is echocurrency ( n/d --> ) |
|||
4000000000000000 5 dollars 50 cents cost |
|||
2 2 dollars 86 cents cost v+ |
|||
say "Total price before tax: " 2dup echocurrency cr |
|||
2dup $ "7.65" tax |
|||
say "Tax: " 2dup echocurrency cr |
|||
v+ |
|||
say "Total price with tax: " echocurrency cr |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Total price before tax: $22000000000000005.72 |
|||
Tax: $1683000000000000.44 |
|||
Total price with tax: $23683000000000006.16</pre> |
|||
=={{header|Racket}}== |
=={{header|Racket}}== |
||
Line 648: | Line 1,769: | ||
The main problem is rounding correctly and this part is handled in cents-*, that implements a multiplication that rounds to the cents. |
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. |
The rest of the program is only formatting. |
||
< |
<syntaxhighlight lang="racket">#lang racket |
||
(define (cents-* x y) |
(define (cents-* x y) |
||
(/ (round (* 100 x y)) 100)) |
(/ (round (* 100 x y)) 100)) |
||
Line 692: | Line 1,813: | ||
(for-each show-item all) |
(for-each show-item all) |
||
(newline) |
(newline) |
||
(show-total all (/ #e7.65 100))</ |
(show-total all (/ #e7.65 100))</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>hamburger 4000000000000000 $5.50 -> $22000000000000000.00 |
<pre>hamburger 4000000000000000 $5.50 -> $22000000000000000.00 |
||
Line 700: | Line 1,821: | ||
tax -> $1683000000000000.44 |
tax -> $1683000000000000.44 |
||
total -> $23683000000000006.16</pre> |
total -> $23683000000000006.16</pre> |
||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
{{works with|Rakudo|2016.01}} |
|||
No need for a special type in Raku, since the <tt>Rat</tt> type is used for normal fractions. |
|||
(In order to achieve imprecision, you have to explicitly use scientific notation, |
|||
or use the <tt>Num</tt> type, or calculate a result that requires a denominator in excess of <tt>2 ** 64</tt>. (There's no limit on the numerator.)) |
|||
<syntaxhighlight lang="raku" line>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> .* $ /, '') }</syntaxhighlight> |
|||
{{out}} |
|||
<pre>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</pre> |
|||
=={{header|REXX}}== |
=={{header|REXX}}== |
||
Line 708: | Line 1,872: | ||
Programming note: the tax rate can be expressed with or without a percent ('''%''') suffix. |
Programming note: the tax rate can be expressed with or without a percent ('''%''') suffix. |
||
===without commas=== |
===without commas=== |
||
< |
<syntaxhighlight lang="rexx">/*REXX program shows a method of computing the total price and tax for purchased items.*/ |
||
numeric digits 200 /*support for gihugic numbers.*/ |
numeric digits 200 /*support for gihugic numbers.*/ |
||
taxRate= 7.65 /*number is: nn or nn% */ |
taxRate= 7.65 /*number is: nn or nn% */ |
||
if right(taxRate, 1)\=='%' then taxRate=taxRate / 100 |
if right(taxRate, 1)\=='%' then taxRate= taxRate / 100 /*handle plain tax rate number*/ |
||
taxRate=strip(taxRate, , '%') |
taxRate= strip(taxRate, , '%') /*strip the % (if present).*/ |
||
item. =; items= |
item. =; items= 0 /*zero out the register. */ |
||
item.1 = '4000000000000000 $5.50 hamburger' /*the first item purchased. */ |
item.1 = '4000000000000000 $5.50 hamburger' /*the first item purchased. */ |
||
item.2 = ' 2 $2.86 milkshake' /* " second " " */ |
item.2 = ' 2 $2.86 milkshake' /* " second " " */ |
||
say center('quantity',22) center("item",22) center('price',22) |
say center('quantity', 22) center("item", 22) center('price', 22) |
||
hdr=center('', 27,"─") center('', 20,"─") center('', 27,"─") |
hdr= center('', 27, "─") center('', 20, "─") center('', 27, "─") |
||
say hdr; total= 0 |
|||
total=0 |
|||
do j=1 while item.j\=='' /*calculate the total and tax.*/ |
do j=1 while item.j\=='' /*calculate the total and tax.*/ |
||
parse var item.j quantity price thing /*ring up an item on register.*/ |
parse var item.j quantity price thing /*ring up an item on register.*/ |
||
items |
items = items + quantity /*tally the number of items. */ |
||
price = translate(price,,'$') |
price = translate(price, , '$') /*maybe scrub out the $ symbol*/ |
||
subtotal = quantity * price /*calculate the sub-total.*/ |
subtotal = quantity * price /*calculate the sub-total.*/ |
||
total = total + subtotal /* " " running total.*/ |
total = total + subtotal /* " " running total.*/ |
||
say right(quantity, 27) left(thing, 20) show$(subtotal) |
say right(quantity, 27) left(thing, 20) show$(subtotal) |
||
end /*j*/ |
end /*j*/ |
||
say /*display a blank line for separator. */ |
say /*display a blank line for separator. */ |
||
say translate(hdr, '═', "─") /*display the separator part of the hdr*/ |
say translate(hdr, '═', "─") /*display the separator part of the hdr*/ |
||
tax=format(total*taxRate,,2) |
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(items "(items)", 35) right('total=', 12) show$(total) |
||
say right('tax at' (taxRate*100/1)"%=", 48) |
say right('tax at' (taxRate * 100 / 1)"%=", 48) show$(tax) |
||
say |
say |
||
say right('grand total=', 48) show$(total+tax) |
say right('grand total=', 48) show$(total+tax) |
||
exit /*stick a fork in it, we're all done. */ |
exit /*stick a fork in it, we're all done. */ |
||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
||
show$: return right( |
show$: return right( '$'arg(1), 27) /*right─justify and format a number. */</syntaxhighlight> |
||
{{out|output|text= (attempting to mimic a check-out register to some degree):}} |
|||
<pre> |
<pre> |
||
quantity item price |
quantity item price |
||
Line 752: | Line 1,916: | ||
===with commas=== |
===with commas=== |
||
< |
<syntaxhighlight lang="rexx">/*REXX program shows a method of computing the total price and tax for purchased items.*/ |
||
numeric digits 200 /*support for gihugic numbers.*/ |
numeric digits 200 /*support for gihugic numbers.*/ |
||
taxRate= 7.65 /*number is: nn or nn% */ |
taxRate= 7.65 /*number is: nn or nn% */ |
||
if right(taxRate, 1)\=='%' then taxRate=taxRate / 100 |
if right(taxRate, 1)\=='%' then taxRate= taxRate / 100 /*handle plain tax rate number*/ |
||
taxRate=strip(taxRate,,'%') |
taxRate=strip(taxRate, , '%') /*strip the % (if present).*/ |
||
item. =; items= |
item. =; items= 0 /*zero out the register. */ |
||
item.1 = '4000000000000000 $5.50 hamburger' /*the first item purchased. */ |
item.1 = '4000000000000000 $5.50 hamburger' /*the first item purchased. */ |
||
item.2 = ' 2 $2.86 milkshake' /* " second " " */ |
item.2 = ' 2 $2.86 milkshake' /* " second " " */ |
||
say center('quantity',22) center("item",22) center('price',22) |
say center('quantity', 22) center("item", 22) center('price', 22) |
||
hdr=center('', 27,"─") center('', 20,"─") center('', 27,"─") |
hdr= center('', 27 ,"─") center('', 20, "─") center('', 27, "─") |
||
say hdr; total=0 |
|||
total=0 |
|||
do j=1 while item.j\=='' /*calculate the total and tax.*/ |
do j=1 while item.j\=='' /*calculate the total and tax.*/ |
||
parse var item.j quantity price thing /*ring up an item on register.*/ |
parse var item.j quantity price thing /*ring up an item on register.*/ |
||
items |
items = items + quantity /*tally the number of items. */ |
||
price = translate(price, , '$') /*maybe scrub out the $ symbol*/ |
price = translate(price, , '$') /*maybe scrub out the $ symbol*/ |
||
subtotal = quantity * price /*calculate the sub-total.*/ |
subtotal = quantity * price /*calculate the sub-total.*/ |
||
total = total + subtotal /* " " running total.*/ |
total = total + subtotal /* " " running total.*/ |
||
say right(quantity, 27) left(thing, 20) show$(subtotal) |
say right(quantity, 27) left(thing, 20) show$(subtotal) |
||
end /*j*/ |
end /*j*/ |
||
say /*display a blank line for separator. */ |
say /*display a blank line for separator. */ |
||
say translate(hdr, '═', "─") /*display the separator part of the hdr*/ |
say translate(hdr, '═', "─") /*display the separator part of the hdr*/ |
||
tax=format(total*taxRate,,2) |
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( commas(items "(items)"), 35) right('total=', 12) show$(total) |
||
say right('tax at' (taxRate*100/1)"%=", 48) show$(tax) |
say right('tax at' (taxRate * 100 / 1)"%=", 48) show$(tax) |
||
say |
say |
||
say right('grand total=', 48) show$(total+tax) |
say right('grand total=', 48) show$(total + tax) |
||
exit /*stick a fork in it, we're all done. */ |
exit /*stick a fork in it, we're all done. */ |
||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
||
commas: procedure; parse arg _; n=_'.9'; #=123456789; b=verify(n,#,"M") |
commas: procedure; parse arg _; n= _'.9'; #= 123456789; b= verify(n, #, "M") |
||
e=verify(n,#'0',,verify(n,#"0.",'M'))-4 |
e= verify(n, #'0', , verify(n, #"0.", 'M') ) - 4 /* [↓] commatize number*/ |
||
do j=e to b by -3; _=insert(',',_,j); end /*j*/; return _ |
do j=e to b by -3; _= insert(',', _, j); end /*j*/; return _ |
||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
||
show$: return right(commas('$' |
show$: return right( commas( '$'arg(1) ), 27) /*right─justify and format a number. */</syntaxhighlight> |
||
{{out|output|text= with commas in the larger numbers:}} |
|||
<pre> |
<pre> |
||
quantity item price |
quantity item price |
||
Line 797: | Line 1,961: | ||
grand total= $23,683,000,000,000,006.16 |
grand total= $23,683,000,000,000,006.16 |
||
</pre> |
|||
=={{header|Ring}}== |
|||
<syntaxhighlight lang="ring"> |
|||
# Project : Currency |
|||
nhamburger = "4000000000" |
|||
phamburger = "5.50" |
|||
nmilkshakes = "2" |
|||
pmilkshakes = "2.86" |
|||
taxrate = "0.0765" |
|||
price = nhamburger * phamburger + nmilkshakes * pmilkshakes |
|||
tax = price * taxrate |
|||
see "total price before tax : " + price + nl |
|||
see "tax thereon @ 7.65 : " + tax + nl |
|||
see "total price after tax : " + (price + tax) + nl |
|||
</syntaxhighlight> |
|||
Output: |
|||
<pre> |
|||
total price before tax : 22000000005.72 |
|||
tax thereon @ 7.65 : 1683000000.44 |
|||
total price after tax : 23683000006.16 |
|||
</pre> |
|||
=={{header|RPL}}== |
|||
Need for big integers. |
|||
{{works with|RPL|HP-49C}} |
|||
« →STR |
|||
'''IF''' LASTARG 9 ≤ '''THEN''' "0" SWAP + '''END''' |
|||
1 OVER SIZE 2 - SUB |
|||
LASTARG NIP 1 + DUP 1 + SUB |
|||
"$" ROT SIZE LASTARG "0" IFTE + |
|||
"." + SWAP + |
|||
» '<span style="color:blue">→CURR</span>' STO |
|||
« 100 * R→I * |
|||
DUP <span style="color:blue">→CURR</span> CLLCD 1 DISP .5 WAIT |
|||
» '<span style="color:blue">→PRICE</span>' STO |
|||
« DUPDUP FLOOR - EVAL →NUM |
|||
0.5 ≥ SWAP CEIL LASTARG FLOOR IFTE |
|||
» '<span style="color:blue">→RND</span>' STO |
|||
« 100 * R→I |
|||
OVER <span style="color:blue">→CURR</span> "TPBT" →TAG |
|||
UNROT OVER * 100000 / <span style="color:blue">→RND</span> DUP <span style="color:blue">→CURR</span> "Tax" →TAG |
|||
UNROT + <span style="color:blue">→CURR</span> "TPWT" →TAG |
|||
» '<span style="color:blue">TAX→</span>' STO |
|||
4000000000000000 5.50 <span style="color:blue">→PRICE</span> |
|||
2 2.86 <span style="color:blue">→PRICE</span> + |
|||
7.65 <span style="color:blue">TAX→</span> |
|||
{{out}} |
|||
<pre> |
|||
3: TPBT:"$22000000000000005.72" |
|||
2: Tax:"$1683000000000000.44" |
|||
1: TPWT:"$23683000000000006.16" |
|||
</pre> |
|||
=={{header|Ruby}}== |
|||
<syntaxhighlight lang="ruby">require 'bigdecimal/util' |
|||
before_tax = 4000000000000000 * 5.50.to_d + 2 * 2.86.to_d |
|||
tax = (before_tax * 0.0765.to_d).round(2) |
|||
total = before_tax + tax |
|||
puts "Before tax: $#{before_tax.to_s('F')} |
|||
Tax: $#{tax.to_s('F')} |
|||
Total: $#{total.to_s('F')}" |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Before tax: $22000000000000005.72 |
|||
Tax: $1683000000000000.44 |
|||
Total: $23683000000000006.16 |
|||
</pre> |
|||
=={{header|Rust}}== |
|||
<syntaxhighlight lang="rust">extern crate num_bigint; // 0.3.0 |
|||
extern crate num_rational; // 0.3.0 |
|||
use num_bigint::BigInt; |
|||
use num_rational::BigRational; |
|||
use std::ops::{Add, Mul}; |
|||
use std::fmt; |
|||
fn main() { |
|||
let hamburger = Currency::new(5.50); |
|||
let milkshake = Currency::new(2.86); |
|||
let pre_tax = hamburger * 4_000_000_000_000_000 + milkshake * 2; |
|||
println!("Price before tax: {}", pre_tax); |
|||
let tax = pre_tax.calculate_tax(); |
|||
println!("Tax: {}", tax); |
|||
let post_tax = pre_tax + tax; |
|||
println!("Price after tax: {}", post_tax); |
|||
} |
|||
#[derive(Debug)] |
|||
struct Currency { |
|||
amount: BigRational, |
|||
} |
|||
impl Add for Currency { |
|||
type Output = Self; |
|||
fn add(self, other: Self) -> Self { |
|||
Self { |
|||
amount: self.amount + other.amount, |
|||
} |
|||
} |
|||
} |
|||
impl Mul<u64> for Currency { |
|||
type Output = Self; |
|||
fn mul(self, other: u64) -> Self { |
|||
Self { |
|||
amount: self.amount * BigInt::from(other), |
|||
} |
|||
} |
|||
} |
|||
impl fmt::Display for Currency { |
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
|||
let cents = (&self.amount * BigInt::from(100)).to_integer(); |
|||
write!(f, "${}.{:0>2}", ¢s / 100, ¢s % 100) |
|||
} |
|||
} |
|||
impl Currency { |
|||
fn new(num: f64) -> Self { |
|||
Self { |
|||
amount: BigRational::new(((num * 100.0).round() as i64).into(), 100.into()) |
|||
} |
|||
} |
|||
fn calculate_tax(&self) -> Self { |
|||
let tax_val = BigRational::new(765.into(), 100.into());// 7,65 -> 0.0765 after the next line |
|||
let amount = (&self.amount * tax_val).ceil() / BigInt::from(100); |
|||
Self { |
|||
amount |
|||
} |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Price before tax: $22000000000000005.72 |
|||
Tax: $1683000000000000.44 |
|||
Price after tax: $23683000000000006.16 |
|||
</pre> |
</pre> |
||
Line 802: | Line 2,119: | ||
{{libheader|Scala}}Locale is manipulated to demonstrate the behavior with other currencies. |
{{libheader|Scala}}Locale is manipulated to demonstrate the behavior with other currencies. |
||
< |
<syntaxhighlight lang="scala">import java.text.NumberFormat |
||
import java.util.Locale |
import java.util.Locale |
||
Line 831: | Line 2,148: | ||
println(f"${" " * 16 + '\t' + " " * 16 + '\t' + f"${tax * 100}%5.2f%% tax" + '\t'}$taxation%,25.2f") |
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") |
println(f"${" " * 16 + '\t' + " " * 16 + '\t' + "Amount due" + '\t'}${beforeTax + taxation}%,25.2f") |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
4000000000000000 Hamburger XL руб. 5,50 22 000 000 000 000 000,00 |
4000000000000000 Hamburger XL руб. 5,50 22 000 000 000 000 000,00 |
||
Line 842: | Line 2,159: | ||
=={{header|Sidef}}== |
=={{header|Sidef}}== |
||
{{trans| |
{{trans|Raku}} |
||
< |
<syntaxhighlight lang="ruby">struct Item { |
||
name, price, quant |
name, price, quant |
||
} |
} |
||
Line 870: | Line 2,187: | ||
var total = subtotal+tax |
var total = subtotal+tax |
||
printf(fmt, '', '', 'Total ', total)</ |
printf(fmt, '', '', 'Total ', total)</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 881: | Line 2,198: | ||
Total 23683000000000006.16 |
Total 23683000000000006.16 |
||
</pre> |
</pre> |
||
=={{header|Smalltalk}}== |
|||
Using ScaledDecimal numbers ('sX'-suffix). |
|||
{{works with|Smalltalk/X}} |
|||
<syntaxhighlight lang="smalltalk">check := #( |
|||
" amount name price " |
|||
(4000000000000000 'hamburger' 5.50s2 ) |
|||
(2 'milkshakes' 2.86s2 ) |
|||
). |
|||
tax := 7.65s2. |
|||
fmt := '%-10s %10P %22P %26P\n'. |
|||
totalSum := 0. |
|||
totalTax := 0. |
|||
Transcript clear. |
|||
Transcript printf:fmt withAll:#('Item' 'Price' 'Qty' 'Extension'). |
|||
Transcript printCR:('-' ,* 72). |
|||
check do:[:entry| |
|||
|amount name price itemTotal itemTax| |
|||
amount := entry[1]. |
|||
name := entry[2]. |
|||
price := entry[3]. |
|||
itemTotal := (price*amount). |
|||
itemTax := ((price*amount)*tax/100) roundedToScale. |
|||
totalSum := totalSum + itemTotal. |
|||
totalTax := totalTax + itemTax. |
|||
Transcript printf:fmt |
|||
withAll:{name . price . amount . itemTotal}. |
|||
]. |
|||
Transcript printCR:('-' ,* 72). |
|||
Transcript printf:fmt withAll:{'' . '' . 'Subtotal' . totalSum}. |
|||
Transcript printf:fmt withAll:{'' . '' . 'Tax' . totalTax}. |
|||
Transcript printf:fmt withAll:{'' . '' . 'Total' . (totalSum+totalTax)}. |
|||
Transcript cr; printCR:('Enjoy your Meal & Thank You for Dining at Milliways')</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Item Price Qty Extension |
|||
------------------------------------------------------------------------ |
|||
hamburger 5.50 4000000000000000 22000000000000000.00 |
|||
milkshakes 2.86 2 5.72 |
|||
------------------------------------------------------------------------ |
|||
Subtotal 22000000000000005.72 |
|||
Tax 1683000000000000.44 |
|||
Total 23683000000000006.16 |
|||
Enjoy your Meal & Thank You for Dining at Milliways</pre> |
|||
=={{header|Swift}}== |
|||
<syntaxhighlight lang="swift">import Foundation |
|||
extension Decimal { |
|||
func rounded(_ scale: Int, _ roundingMode: NSDecimalNumber.RoundingMode) -> Decimal { |
|||
var result = Decimal() |
|||
var localCopy = self |
|||
NSDecimalRound(&result, &localCopy, scale, roundingMode) |
|||
return result |
|||
} |
|||
} |
|||
let costHamburgers = Decimal(4000000000000000) * Decimal(5.50) |
|||
let costMilkshakes = Decimal(2) * Decimal(2.86) |
|||
let totalBeforeTax = costMilkshakes + costHamburgers |
|||
let taxesToBeCollected = (Decimal(string: "0.0765")! * totalBeforeTax).rounded(2, .bankers) |
|||
print("Price before tax: $\(totalBeforeTax)") |
|||
print("Total tax to be collected: $\(taxesToBeCollected)") |
|||
print("Total with taxes: $\(totalBeforeTax + taxesToBeCollected)")</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Price before tax: $22000000000000005.72 |
|||
Total tax to be collected: $1683000000000000.44 |
|||
Total with taxes: $23683000000000006.16</pre> |
|||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
||
{{tcllib|math::decimal}}< |
{{tcllib|math::decimal}}<syntaxhighlight lang="tcl">package require math::decimal |
||
namespace import math::decimal::* |
namespace import math::decimal::* |
||
Line 896: | Line 2,291: | ||
set total [+ $net $tax] |
set total [+ $net $tax] |
||
puts "net=[tostr $net], tax=[tostr $tax], total=[tostr $total]"</ |
puts "net=[tostr $net], tax=[tostr $tax], total=[tostr $total]"</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
net=22000000000000005.72, tax=1683000000000000.44, total=23683000000000006.16 |
net=22000000000000005.72, tax=1683000000000000.44, total=23683000000000006.16 |
||
=={{header|Unicon}}== |
|||
Solution takes advantage of Unicon's FxPt class as well as Unicon's operator overloading extension. |
|||
<syntaxhighlight lang="unicon">import math |
|||
procedure main() |
|||
n_burgers := 4000000000000000 |
|||
n_shakes := 2 |
|||
price := FxPt(5.50) * n_burgers + FxPt(2.86) * n_shakes |
|||
tax := (price * FxPt(7.65/100)).round(2) |
|||
total := price + tax |
|||
write(left("Price", 10), "$", right(price.toString(),21)) |
|||
write(left("Tax", 10), "$", right(tax.toString(),21)) |
|||
write(left("Total", 10), "$", right(total.toString(),21)) |
|||
end</syntaxhighlight>{{out}} |
|||
<pre>Price $ 22000000000000005.72 |
|||
Tax $ 1683000000000000.44 |
|||
Total $ 23683000000000006.16</pre> |
|||
=={{header|VBA}}== |
|||
Used in locality Euroland. Formatting as currency shows € in stead of $, and thousand separators are "." in stead of "," and the decimail 'point' is "," in stead of "." When run in the USA it will be with dollar sign and so on. |
|||
<syntaxhighlight lang="vb">Public Sub currency_task() |
|||
'4000000000000000 hamburgers at $5.50 each |
|||
Dim number_of_hamburgers As Variant |
|||
number_of_hamburgers = CDec(4E+15) |
|||
Dim price_of_hamburgers As Currency |
|||
price_of_hamburgers = 5.5 |
|||
'2 milkshakes at $2.86 each, and |
|||
Dim number_of_milkshakes As Integer |
|||
number_of_milkshakes = 2 |
|||
Dim price_of_milkshakes As Currency |
|||
price_of_milkshakes = 2.86 |
|||
'a tax rate of 7.65%. |
|||
Dim tax_rate As Single |
|||
tax_rate = 0.0765 |
|||
'the total price before tax |
|||
Dim total_price_before_tax As Variant |
|||
total_price_before_tax = number_of_hamburgers * price_of_hamburgers |
|||
total_price_before_tax = total_price_before_tax + number_of_milkshakes * price_of_milkshakes |
|||
Debug.Print "Total price before tax "; Format(total_price_before_tax, "Currency") |
|||
'the tax |
|||
Dim tax As Variant |
|||
tax = total_price_before_tax * tax_rate |
|||
Debug.Print "Tax "; Format(tax, "Currency") |
|||
'the total with tax |
|||
Debug.Print "Total with tax "; Format(total_price_before_tax + tax, "Currency") |
|||
End Sub</syntaxhighlight>{{out}} |
|||
<pre>Total price 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</pre> |
|||
=={{header|Wren}}== |
|||
{{libheader|Wren-big}} |
|||
<syntaxhighlight lang="wren">import "./big" for BigRat |
|||
var hamburgers = BigRat.new("4000000000000000") |
|||
var milkshakes = BigRat.two |
|||
var price1 = BigRat.fromFloat(5.5) |
|||
var price2 = BigRat.fromFloat(2.86) |
|||
var taxPc = BigRat.fromFloat(0.0765) |
|||
var totalPc = BigRat.fromFloat(1.0765) |
|||
var totalPreTax = hamburgers*price1 + milkshakes*price2 |
|||
var totalTax = taxPc * totalPreTax |
|||
var totalAfterTax = totalPreTax + totalTax |
|||
System.print("Total price before tax : %((totalPreTax).toDecimal(2))") |
|||
System.print("Tax : %((totalTax).toDecimal(2))") |
|||
System.print("Total price after tax : %((totalAfterTax).toDecimal(2))")</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Total price before tax : 22000000000000005.72 |
|||
Tax : 1683000000000000.44 |
|||
Total price after tax : 23683000000000006.16 |
|||
</pre> |
|||
=={{header|zkl}}== |
=={{header|zkl}}== |
||
Line 904: | Line 2,375: | ||
So, just multiply bucks by 100 and be careful when doing tax calculations. |
So, just multiply bucks by 100 and be careful when doing tax calculations. |
||
{{trans|Python}} |
{{trans|Python}} |
||
< |
<syntaxhighlight lang="zkl">var priceList=Dictionary("hamburger",550, "milkshake",286); |
||
var taxRate=765; // percent*M |
var taxRate=765; // percent*M |
||
const M=0d10_000; |
const M=0d10_000; |
||
Line 929: | Line 2,400: | ||
fmt.fmt("","","","--------------------").println(); |
fmt.fmt("","","","--------------------").println(); |
||
fmt.fmt("","","Total",toBucks(totalBeforeTax + tax)).println(); |
fmt.fmt("","","Total",toBucks(totalBeforeTax + tax)).println(); |
||
}</ |
}</syntaxhighlight> |
||
< |
<syntaxhighlight lang="zkl">calcTab(T("milkshake",2),T("hamburger",4000000000000000));</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
Latest revision as of 09:09, 8 May 2024
You are encouraged to solve this task according to the task description, using any language you may know.
- 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.
11l
F currency(units, subunits)
R BigInt(units) * 100 + subunits
F currency_from_str(s)
V (units, subunits) = s.split(‘.’)
R BigInt(units) * 100 + Int(subunits)
F percentage(a, num, denom)
R (a * num * 10 I/ denom + 5) I/ 10
F to_str(c)
R String(c I/ 100)‘’‘.#02’.format(c % 100)
V hamburgers = currency(5, 50) * 4'000'000'000'000'000
V milkshakes = currency_from_str(‘2.86’) * 2
V beforeTax = hamburgers + milkshakes
V tax = percentage(beforeTax, 765, 10'000)
V total = beforeTax + tax
V maxlen = max(to_str(beforeTax).len, to_str(tax).len, to_str(total).len)
print(‘Total price before tax: ’to_str(beforeTax).rjust(maxlen))
print(‘Tax: ’to_str(tax).rjust(maxlen))
print(‘Total with tax: ’to_str(total).rjust(maxlen))
- Output:
Total price before tax: 22000000000000005.72 Tax: 1683000000000000.44 Total with tax: 23683000000000006.16
Ada
Numeric constants in Ada do not have to be given an explicit type. When used in constant expressions together, they have arbitrary precision. This enables simple calculation of the values. Ada also has builtin fixed-point types, which can define a minimum interval between decimal values (in this case, 1 cent or $0.01).
When using the Dollar_IO package to print each value, the constant is converted into a Dollar, then printed. All of the arbitrary-precision arithmetic operations are done at compile-time, incurring no runtime cost.
with Ada.Text_IO;
procedure Currency is
type Dollar is delta 0.01 range 0.0 .. 24_000_000_000_000_000.0;
package Dollar_IO is new Ada.Text_IO.Fixed_IO(Dollar);
hamburger_cost : constant := 5.50;
milkshake_cost : constant := 2.86;
tax_rate : constant := 0.0765;
total_cost : constant := hamburger_cost * 4_000_000_000_000_000.0 + milkshake_cost * 2;
total_tax : constant := total_cost * tax_rate;
total_with_tax : constant := total_cost + total_tax;
begin
Ada.Text_IO.Put("Price before tax:");
Dollar_IO.Put(total_cost);
Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Tax: ");
Dollar_IO.Put(total_tax);
Ada.Text_IO.New_Line;
Ada.Text_IO.Put("Total: ");
Dollar_IO.Put(total_with_tax);
Ada.Text_IO.New_Line;
end Currency;
- Output:
Price before tax: 22000000000000005.72 Tax: 1683000000000000.44 Total: 23683000000000006.16
ALGOL 68
BEGIN
# currency calculations #
# simple fixed point type, LONG INT is 64-bit in Algol 68G #
MODE FIXED = STRUCT( LONG INT value
, INT decimals
, INT fraction modulus
);
# make CURRENCY a synonym for FIXED #
MODE CURRENCY = FIXED;
# dyadic operator so we can write e.g. 5 DOLLARS 50 to construct #
# a CURRENCY value with 2 decimal places #
PRIO DOLLARS = 9;
OP DOLLARS = ( LONG INT v, INT dp )CURRENCY: ( ( v * 100 ) + dp, 2, 100 );
OP DOLLARS = ( INT v, INT dp )CURRENCY: LENG v DOLLARS dp;
# issues an error message and stops the program if a has a different #
# number of decimal places to b #
PROC check compatible = ( CURRENCY a, b )VOID:
IF decimals OF a /= decimals OF b THEN print( ( "Incompatible CURRENCY values", newline ) ); stop FI;
# operators to multiply CURRENCY values by integers #
OP * = ( CURRENCY v, LONG INT m )CURRENCY: ( value OF v * m, decimals OF v, fraction modulus OF v );
OP * = ( CURRENCY v, INT m )CURRENCY: v * LENG m;
# returns the CURRENCY value a + b #
OP + = ( CURRENCY a, CURRENCY b )CURRENCY:
BEGIN
check compatible( a, b );
( value OF a + value OF b, decimals OF a, fraction modulus OF a )
END # + # ;
# multiplies the CURRENCY value a by the FIXED value b, #
# rounding the result to the decimal places of a #
OP * = ( CURRENCY a, FIXED b )CURRENCY:
BEGIN
LONG INT result := ( value OF a * value OF b );
IF decimals OF b > 0 THEN
INT d = fraction modulus OF b;
LONG INT abs result := ABS result;
INT extra places = SHORTEN ( abs result MOD d );
abs result OVERAB d;
IF extra places >= d OVER 2 THEN abs result +:= 1 FI;
IF result < 0 THEN result := - abs result ELSE result := abs result FI
FI;
( result, decimals OF a, fraction modulus OF a )
END # * # ;
# converts a FIXED value to a STRING with the appropriate number of #
# decimal places #
OP TOSTRING = ( FIXED v )STRING:
IF decimals OF v < 1 THEN
whole( value OF v, 0 )
ELSE
INT d = fraction modulus OF v;
STRING result := whole( value OF v OVER d, 0 );
STRING dp := whole( ( ABS value OF v ) MOD d, - decimals OF v );
FOR i FROM LWB dp TO UPB dp DO IF dp[ i ] = " " THEN dp[ i ] := "0" FI OD;
result + "." + dp
FI # TOSTRING # ;
# Task calculations #
CURRENCY hb = 5 DOLLARS 50 * LONG 4000000000000000;
CURRENCY ms = 2 DOLLARS 86 * 2;
FIXED rate = ( 765, 4, 10 000 ); # 0.0765 #
CURRENCY net = hb + ms;
CURRENCY tax = net * rate;
CURRENCY total = net + tax;
print( ( "before tax: ", TOSTRING net, newline ) );
print( ( "tax: ", TOSTRING tax, newline ) );
print( ( "total: ", TOSTRING total, newline ) )
END
- Output:
before tax: 22000000000000005.72 tax: 1683000000000000.44 total: 23683000000000006.16
AppleScript
The AppleScript core language doesn't recognise currency values specifically and its numbers don't have the precision required for this task, but through ASObjC, it's able to use Foundation classes which do.
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)
- Output:
"Subtotal: $22,000,000,000,000,005.72
Tax: $1,683,000,000,000,000.44
Total: $23,683,000,000,000,006.16"
AWK
version 1
# 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
# 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
BBC BASIC
REM No need for BigNum library.
REM This language uses 80bit (10 bytes!) for real values internally.
Price = 4E15 * 5.50 + 2.0 * 2.86
Tax = Price * .0765
Total = Price + Tax
REM Number printing will use 2 decimal places and 21 positions zone
@%=&020215
PRINT "Price = $" Price
PRINT "Tax = $" Tax
PRINT "Total = $" Total
- Output:
Price = $ 22000000000000005.72 Tax = $ 1683000000000000.44 Total = $ 23683000000000006.16
Bracmat
The amounts before-tax
, tax
, after-tax
are computed as rounded amounts in dollar cents.
div$((4000000000000000*550+2*286)+1/2,1):?before-tax
& div$(!before-tax*765/10000+1/2,1):?tax
& !before-tax+!tax:?after-tax
& ( fix
= cents dollars
. mod$(!arg.100):?cents
& ( !cents:<10&0 !cents:?cents
|
)
& div$(!arg.100):?dollars
& str$(!dollars "." !cents)
)
& str
$ ( "before-tax "
fix$!before-tax
"\ntax "
fix$!tax
\n
"after-tax "
fix$!after-tax
\n
)
Output
before-tax 22000000000000005.72 tax 1683000000000000.44 after-tax 23683000000000006.16
C
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 :)
#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#
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
(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
(require '[io.randomseed.bankster.money :as m])
(let [burgers (m/mul #money[USD 5.50] 4000000000000000)
milkshakes (m/mul #money[USD 2.86] 2)
pre-tax (m/add burgers milkshakes)
tax (m/with-rounding UP (m/mul pre-tax 0.0765))]
(println "Total before tax: " (m/format pre-tax))
(println " Tax: " (m/format tax))
(println " Total with tax: " (m/format (m/add 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
COBOL supports up to 31 digits of decimal precision, so won't need any fancy currency/BigInteger types!
During calculations the default ROUNDED MODE IS clause, when specified, is NEAREST-AWAY-FROM-ZERO. When ROUNDED is not specified, the default mode is TRUNCATION. The term "banker's rounding" implies NEAREST-EVEN.
>>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 rounded, total-edited rounded =
num-burgers * Burger-Price + num-milkshakes * Milkshake-Price
DISPLAY "Total before tax: " total-edited
COMPUTE tax rounded, tax-edited rounded = total * (Tax-Rate / 100)
DISPLAY " Tax: " tax-edited
ADD tax TO total GIVING total-edited rounded
DISPLAY " Total with tax: " total-edited
.
END PROGRAM currency-example.
- Output:
Total before tax: $22000000000000005.72 Tax: $1683000000000000.44 Total with tax: $23683000000000006.16
Common Lisp
Let us just use the built-in and convenient rationals (which use two bignums for numerator and denominator).
(defun print-$ (rat &key (prefix "") (stream t))
(multiple-value-bind (dollars cents) (truncate rat)
(format stream "~A~D.~D~%" prefix dollars (round (* 100 cents)))))
(defun compute-check (order-alist tax-rate)
(let* ((total-before-tax
(loop :for (amount . price) in order-alist
:sum (* (rationalize price) amount)))
(tax (* (rationalize tax-rate) total-before-tax)))
(print-$ total-before-tax :prefix "Total before tax: ")
(print-$ tax :prefix "Tax: ")
(print-$ (+ total-before-tax tax) :prefix "Total with tax: ")))
(compute-check '((4000000000000000 . 5.5) (2 . 2.86)) 0.0765)
- Output:
Total before tax: 22000000000000005.72 Tax: 1683000000000000.44 Total with tax: 23683000000000006.16
A reader macro can be used as extra nice icing on the cake:
(defun read-$ (stream char)
(declare (ignore char))
(let* ((str (with-output-to-string (out)
;; TODO: read integer, if dot, read dot and another integer
(loop :for next = (peek-char nil stream t nil t)
:while (or (digit-char-p next) (char= next #\.))
:do (write-char (read-char stream t nil t) out))))
(dot-pos (position #\. str))
(dollars (parse-integer str :end dot-pos))
(cents (if dot-pos
(/ (parse-integer str :start (+ dot-pos 1))
(expt 10 (- (length str) (+ dot-pos 1))))
0)))
(+ dollars cents)))
(set-macro-character #\$ #'read-$ t)
(defun print-$ (rat &key (prefix "") (stream t))
(multiple-value-bind (dollars cents) (truncate rat)
(format stream "~A~D.~D~%" prefix dollars (round (* 100 cents)))))
(defun compute-check (order-alist tax-rate)
(let* ((total-before-tax
(loop :for (amount . price) in order-alist
:sum (* price amount)))
(tax (* (rationalize tax-rate) total-before-tax)))
(print-$ total-before-tax :prefix "Total before tax: ")
(print-$ tax :prefix "Tax: ")
(print-$ (+ total-before-tax tax) :prefix "Total with tax: ")))
(compute-check '((4000000000000000 . $5.5) (2 . $2.86)) 0.0765)
Delphi
program Currency;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
Velthuis.BigRationals,
Velthuis.BigDecimals,
Velthuis.BigIntegers;
var
one: BigInteger;
hundred: BigInteger;
half: BigRational;
type
TDc = record
value: BigInteger;
function ToString: string;
function Extend(n: BigInteger): TDc;
class operator Add(a, b: TDc): TDc;
end;
TTR = record
value: BigRational;
function SetString(const s: string; var TR: TTR): boolean;
function Tax(dc: TDc): TDc;
end;
{ TDc }
// Extend returns extended price of a unit price.
class operator TDc.Add(a, b: TDc): TDc;
begin
Result.value := a.value + b.value;
end;
function TDc.Extend(n: BigInteger): TDc;
begin
Result.value := n * value;
end;
function TDc.ToString: string;
var
d: BigInteger;
begin
d := value.Divide(value, 100);
if value < 0 then
value := -value;
Result := Format('%s.%2s', [d.ToString, (value mod 100).ToString]);
end;
// ParseDC parses dollars and cents as a string into a DC.
function ParseDC(s: string; var Dc: TDc): Boolean;
var
r: BigRational;
d: BigDecimal;
begin
Result := d.TryParse(s, d);
if not Result then
begin
Dc.value := 0;
exit(false);
end;
r := r.Create(d);
r := r.Multiply(r, 100);
if BigInteger.Compare(r.Denominator, 1) <> 0 then
begin
Dc.value := 0;
exit(false);
end;
Result := true;
Dc.value := r.Numerator;
end;
{ TTR }
function TTR.SetString(const s: string; var TR: TTR): boolean;
var
d: BigDecimal;
begin
Result := d.TryParse(s, d);
if Result then
TR.value := BigRational.Create(d);
end;
function TTR.Tax(dc: TDc): TDc;
var
r: BigRational;
i: BigInteger;
begin
r := BigRational.Create(dc.value, 1);
r := r.Multiply(r, self.value);
r := r.add(r, half);
i := i.Divide(r.Numerator, r.Denominator);
Result.value := i;
end;
var
hamburgerPrice, milkshakePrice, totalBeforeTax, tax, total: TDc;
taxRate: TTR;
begin
one := 1;
hundred := 100;
half := BigRational.Create(1, 2);
if not ParseDC('5.50', hamburgerPrice) then
begin
Writeln('Invalid hamburger price');
halt(1);
end;
if not ParseDC('2.86', milkshakePrice) then
begin
Writeln('Invalid milkshake price');
halt(2);
end;
if not taxRate.SetString('0.0765', taxRate) then
begin
Writeln('Invalid tax rat');
halt(3);
end;
totalBeforeTax := hamburgerPrice.Extend(4000000000000000) + milkshakePrice.Extend(2);
tax := taxRate.Tax(totalBeforeTax);
total := totalBeforeTax + tax;
Writeln('Total before tax: ', totalBeforeTax.ToString: 22);
Writeln(' Tax: ', tax.ToString: 22);
Writeln(' Total: ', total.ToString: 22);
readln;
end.
F#
open System
let hamburgers = 4000000000000000M
let hamburgerPrice = 5.50M
let milkshakes = 2M
let milkshakePrice = 2.86M
let taxRate = 0.0765M
let total = hamburgers * hamburgerPrice + milkshakes * milkshakePrice
let tax = total * taxRate
let totalWithTax = total + tax
printfn "Total before tax:\t$%M" <| Math.Round (total, 2)
printfn " Tax:\t$%M" <| Math.Round (tax, 2)
printfn " Total:\t$%M" <| Math.Round (totalWithTax, 2)
- Output:
Total before tax: $22000000000000005.72 Tax: $1683000000000000.44 Total with tax: $23683000000000006.16
Factor
Factor's ratio type can handle arbitrary-precision calculations with rational numbers. The money vocabulary implements convenience words for treating rationals as money. The DECIMAL: parsing word is used to convert the tax rate 0.0765 to a ratio 153/2000. The money. word is used to print the subtotal 22000000000000005+18/25, tax 1683000000000000+21879/50000, and total 23683000000000006+7879/50000 formatted as you would expect.
USING: combinators.smart io kernel math math.functions money ;
10 15 ^ 4 * 5+50/100 * ! hamburger subtotal
2 2+86/100 * ! milkshake subtotal
+ ! subtotal
dup DECIMAL: 0.0765 * ! tax
[ + ] preserving ! total
"Total before tax: " write [ money. ] 2dip
"Tax: " write [ money. ] dip
"Total with tax: " write money.
- Output:
Total price 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
FreeBASIC
Pero el subtotal y el tax los muestra redondeados. Y no encuentro el porqué.
Dim As Longint hamburger_p = 550
Dim As Longint hamburger_q = 4000000000000000
Dim As Longint hamburger_v = hamburger_p * hamburger_q
Dim As Longint milkshake_p = 286
Dim As Longint milkshake_q = 2
Dim As Longint milkshake_v = milkshake_p * milkshake_q
Dim As Longint subtotal = hamburger_v + milkshake_v
Dim As Longint tax = subtotal * .765
Print Using "\ \ \ \ \ \ \ \";"item";"price";"quantity";"value"
Print Using "hamburger ##.## ################ #####################.##";hamburger_p/100;hamburger_q;hamburger_v/100
Print Using "milkshake ##.## ################ #####################.##";milkshake_p/100;milkshake_q;milkshake_v/100
?
Print Using " subtotal #####################.##";subtotal/10
Print Using " tax #####################.##";tax/100
Print Using " total #####################.##";subtotal/10+tax/100
Sleep
Frink
Frink tracks units of measure through all calculations, so you can specify quantities as "dollar", "cent", etc.
st = 4000000000000000 * 5.50 dollars + 2 * 2.86 dollars
tax = round[st * 7.65 percent, cent]
total = st + tax
println["Subtotal: " + format[st, "dollars", 2]]
println["Tax: " + format[tax, "dollars", 2]]
println["Total: " + format[total, "dollars", 2]]
- Output:
Subtotal: 22000000000000005.72 dollars Tax: 1683000000000000.44 dollars Total: 23683000000000006.16 dollars
FutureBasic
local fn Lunch_Invoice( burger_price as CFStringRef, burger_amount as CFStringRef, shake_price as CFStringRef, shake_amount as CFStringRef, tax as CFStringRef )
'~'1
DecimalNumberRef burgerPriceDecimal = fn DecimalNumberWithString( burger_price )
DecimalNumberRef burgerAmountDecimal = fn DecimalNumberWithString( burger_amount )
DecimalNumberRef burgersDecimal = fn DecimalNumberByMultiplyingBy( burgerPriceDecimal, burgerAmountDecimal )
DecimalNumberRef shakePriceDecimal = fn DecimalNumberWithString( shake_price )
DecimalNumberRef shakeAmountDecimal = fn DecimalNumberWithString( shake_amount )
DecimalNumberRef shakesDecimal = fn DecimalNumberByMultiplyingBy( shakePriceDecimal, shakeAmountDecimal )
DecimalNumberRef taxDecimal = fn DecimalNumberWithString( tax )
DecimalNumberRef subtotalDecimal = fn DecimalNumberByAdding( burgersDecimal, shakesDecimal )
DecimalNumberRef taxTotalDecimal = fn DecimalNumberByMultiplyingBy( subtotalDecimal, taxDecimal )
DecimalNumberRef adjTaxTotalDecimal = fn DecimalNumberByAdding( taxTotalDecimal, fn DecimalNumberWithString( @"0.01" ) )
DecimalNumberRef billTotalDecimal = fn DecimalNumberByAdding( subtotalDecimal, adjTaxTotalDecimal )
CFStringRef burgersString = fn DecimalNumberString( burgersDecimal )
CFStringRef shakesString = fn DecimalNumberString( shakesDecimal )
CFStringRef taxTotalString = fn DecimalNumberString( adjTaxTotalDecimal )
CFStringRef billTotalString = fn DecimalNumberString( billTotalDecimal )
printf @"%@", fn StringByPaddingToLength( @"", 55, @"-", 0 )
printf @"Item Price Quantity Cost"
printf @"Hamburgers %6s %18s %18s", fn StringUTF8String( burger_price ), fn StringUTF8String( burger_amount ), fn StringUTF8String( burgersString )
printf @"Milkshakes %6s %18s %18s", fn StringUTF8String( shake_price ), fn StringUTF8String( shake_amount ), fn StringUTF8String( shakesString )
printf @"%@", fn StringByPaddingToLength( @"", 55, @"-", 0 )
printf @"%34s %@", fn StringUTF8String( @"Subtotal:" ), fn DecimalNumberString( subtotalDecimal )
printf @"%35s %@", fn StringUTF8String( @" Tax: " ), fn StringSubstringToIndex( taxTotalString, len(taxTotalString) - 3 )
printf @"%34s %@", fn StringUTF8String( @" Total:" ), fn StringSubstringToIndex( billTotalString, len(billTotalString) - 3 )
end fn
NSLog( @"%@", fn WindowPrintViewString( 1 ) )
HandleEvents
- Output:
------------------------------------------------------- Item Price Quantity Cost Hamburgers 5.50 4000000000000000 22000000000000000 Milkshakes 2.86 2 5.72 ------------------------------------------------------- Subtotal: 22000000000000005.72 Tax: 1683000000000000.44 Total: 23683000000000006.16
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)
}
- Output:
Total before tax: 22000000000000005.72 Tax: 1683000000000000.44 Total: 23683000000000006.16
Haskell
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
We use a naive implementation with arbitrary precision (rational) numbers:
require 'format/printf'
Items=: ;: 'Hamburger Milkshake'
Quantities=: 4000000000000000 2
Prices=: x: 5.50 2.86
Tax_rate=: x: 0.0765
makeBill=: verb define
'items prices quantities'=. y
values=. prices * quantities
subtotal=. +/ values
tax=. Tax_rate * subtotal
total=. subtotal + tax
'%9s %8s %20s %22s' printf ;:'Item Price Quantity Value'
'%9s %8.2f %20d %22.2f' printf"1 items ,. <"0 prices ,. quantities ,. values
'%62s' printf <'-------------------------------'
'%40s %21.2f' printf"1 (;:'Subtotal: Tax: Total:') ,. subtotal;tax;total
)
makeBill Items;Prices;Quantities
- 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
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
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)
jq
For simplicity, we will use the gojq implementation of jq as it supports unbounded-precision integer arithmetic.
If the standard implementation of jq were used, then it would probably be simplest to use the BigInt.jq library, which uses strings for precision.
The strategy here is to use cents except when presenting results as dollars and cents, and when calculating the taxes.
def lpad($len): tostring | ($len - length) as $l | (" " * $l)[:$l] + .;
# print as dollars and cents
def dollars:
(. % 100) as $c
| "$\((. - $c) /100).\($c)";
def dollars($width):
dollars | lpad($width);
def innerproduct($y):
. as $x
| reduce range(0;$x|length) as $i (0; . + ($x[$i]*$y[$i]));
def plus($y):
. as $x
| reduce range(0;$x|length) as $i ([]; .[$i] = ($x[$i]+$y[$i]));
# Round up or down
def integer_division($y):
(. % $y) as $remainder
| (. - $remainder) / $y
| if $remainder * 2 > $y then . + 1 else . end;
# For computing taxes
def precision: 10000;
def cents: integer_division(precision);
### The task:
def p: [550, 286];
def q: [4000000000000000, 2];
def taxrate: 765; # relative to `precision`
(p | innerproduct(q)) as $before_tax # cents
| ($before_tax * taxrate) as $taxes # relative to precision
| ((($before_tax * precision) + $taxes) | cents) as $after_tax # cents
| ($after_tax|tostring|length + 2) as $width
|
" Total before tax: \($before_tax | dollars($width))",
" - tax: \($taxes | cents | dollars($width))",
" Total after tax: \($after_tax | dollars($width))"
- Output:
Total before tax: $22000000000000005.72 - tax: $1683000000000000.44 Total after tax: $23683000000000006.16
Julia
using Printf
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
// 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
Lua
Using the lbc library for arbitrary precision support.
C = setmetatable(require("bc"), {__call=function(t,...) return t.new(...) end})
C.digits(6) -- enough for .nn * .nnnn ==> .nnnnnn, follow with trunc(2) to trim trailing zeroes
subtot = (C"4000000000000000" * C"5.50" + C"2" * C"2.86"):trunc(2) -- cosmetic trunc
tax = (subtot * C"0.0765" + C"0.005"):trunc(2) -- rounding trunc
total = (subtot + tax):trunc(2) -- cosmetic trunc
print(("Before tax: %20s"):format(subtot:tostring()))
print(("Tax : %20s"):format(tax:tostring()))
print(("With tax : %20s"):format(total:tostring()))
- Output:
Before tax: 22000000000000005.72 Tax : 1683000000000000.44 With tax : 23683000000000006.16
M2000 Interpreter
This task written in M2000 Environment running on Wine 3.6, in a Linux Ubuntu Studio. M2000 environment is an ActiveX object, written with VB6, which use many types from COM Variant Type.
Module Currency_Task {
Locale 1033
Font "Courier New"
Form 80,32
\\Decimal type
hamburgers=4000000000000000@
\\ Currency type
hamburger_price=5.5#
milkshakes=2#
milkshake_price=2.86#
tax_rate=0.0765#
\\ Using Columns with variable width in console
PrHeadLine("Item","price","quantity", "value")
PrLine("hamburger",hamburger_price,hamburgers,hamburgers*hamburger_price)
PrLine("milkshake", milkshake_price,milkshakes,milkshakes*milkshake_price)
PrResults( "subtotal", hamburgers*hamburger_price+milkshakes*milkshake_price)
PrResults("tax", (hamburgers*hamburger_price+milkshakes*milkshake_price)*tax_rate)
\\ 1 is double by default we can use 1# or 1@
PrResults("total", (hamburgers*hamburger_price+milkshakes*milkshake_price)*(tax_rate+1))
\\ Using variables for partial calculations. They get type from expression result
h_p_q=hamburgers*hamburger_price
m_p_q=milkshakes*milkshake_price
\\ Using format$ to prepare final strings
Print format$("{0:15}{1:-8}{2:-25}{3:-25}","Item", "price", "quantity", "value")
Print format$("{0:15}{1:2:-8}{2:0:-25}{3:2:-25}","hamburger",hamburger_price,hamburgers, h_p_q)
Print format$("{0:15}{1:2:-8}{2:0:-25}{3:2:-25}","milkshake", milkshake_price,milkshakes,m_p_q)
Print format$("{0:-48}{1:2:-25}","subtotal", h_p_q+m_p_q)
Print format$("{0:-48}{1:2:-25}","tax", (h_p_q+m_p_q)*tax_rate)
Print format$("{0:-48}{1:2:-25}","total", (h_p_q+m_p_q)*(tax_rate+1))
\\ Another time to feed Document to export to clipboard
Document Doc$=format$("{0:15}{1:-8}{2:-25}{3:-25}","Item", "price", "quantity", "value")+{
}+format$("{0:15}{1:2:-8}{2:0:-25}{3:2:-25}","hamburger",hamburger_price,hamburgers, h_p_q)+{
}+format$("{0:15}{1:2:-8}{2:0:-25}{3:2:-25}","milkshake", milkshake_price,milkshakes,m_p_q)+{
}+format$("{0:-48}{1:2:-25}","subtotal", h_p_q+m_p_q)+{
}+format$("{0:-48}{1:2:-25}","tax", (h_p_q+m_p_q)*tax_rate)+{
}+format$("{0:-48}{1:2:-25}","total", (h_p_q+m_p_q)*(tax_rate+1))+{
}
clipboard Doc$
\\ one line user function definition
\\ x get type from passed value
Def ExpressionType$(x)=Type$(X)
\\ Check Expression final type
Print ExpressionType$(hamburgers)="Decimal"
Print ExpressionType$(milkshakes)="Currency"
Print ExpressionType$(h_p_q)="Decimal"
Print ExpressionType$(m_p_q)="Currency"
Print ExpressionType$((h_p_q+m_p_q)*tax_rate)="Decimal"
Print ExpressionType$((h_p_q+m_p_q)*(tax_rate+1))="Decimal"
Sub PrHeadLine(a$,b$,c$,d$)
Print Part $(1,15),a$,$(3,8),b$, $(3,25),c$, $(3,25),d$
Print
End Sub
Sub PrLine(a$,b,c,d)
Print Part $(1,15),a$,$("0.00"),$(3,8),b, $("0"),$(3,25),c,$("0.00"), $(3,25),d
Print
End Sub
Sub PrResults(a$,b)
Print Part $(3,48),a$,$("0.00"),$(3,25),b
Print
End Sub
}
Currency_Task
Optional with $ and thousands separator (a smaller version from above)
Module Currency_Task {
Locale 1033
Font "Courier New"
Form 80,32
hamburgers=4000000000000000@
hamburger_price=5.5#
milkshakes=2#
milkshake_price=2.86#
tax_rate=0.0765#
PrHeadLine("Item","price","quantity", "value")
PrLine("hamburger",hamburger_price,hamburgers,hamburgers*hamburger_price)
PrLine("milkshake", milkshake_price,milkshakes,milkshakes*milkshake_price)
PrResults( "subtotal", hamburgers*hamburger_price+milkshakes*milkshake_price)
PrResults("tax", (hamburgers*hamburger_price+milkshakes*milkshake_price)*tax_rate)
PrResults("total", (hamburgers*hamburger_price+milkshakes*milkshake_price)*(tax_rate+1))
h_p_q=hamburgers*hamburger_price
m_p_q=milkshakes*milkshake_price
Document Doc$=format$("{0:15}{1:-8}{2:-25}{3:-30}","Item", "price", "quantity", "value")+{
}+format$("{0:15}{1:-8}{2:-25}{3:-30}","hamburger",str$(hamburger_price,"$#,##0.00"),str$(hamburgers, "#,##0"), Str$(h_p_q,"$#,##0.00"))+{
}+format$("{0:15}{1:-8}{2:-25}{3:-30}","milkshake", str$(milkshake_price,"$#,##0.00"),Str$(milkshakes, "#,##0"), Str$(m_p_q,"$#,##0.00"))+{
}+format$("{0:-48}{1:-30}","subtotal", Str$(h_p_q+m_p_q,"$#,##0.00"))+{
}+format$("{0:-48}{1:-30}","tax", Str$((h_p_q+m_p_q)*tax_rate,"$#,##0.00"))+{
}+format$("{0:-48}{1:-30}","total", Str$((h_p_q+m_p_q)*(tax_rate+1),"$#,##0.00"))+{
}
clipboard Doc$
Sub PrHeadLine(a$,b$,c$,d$)
Print Part $(1,15),a$,$(3,8),b$, $(3,25),c$, $(3,30),d$
Print
End Sub
Sub PrLine(a$,b,c,d)
Print Part $(1,15),a$,$("$#,###.00"),$(3,8),b, $("#,##0"),$(3,25),c,$("$#,###.00"), $(3,30),d
Print
End Sub
Sub PrResults(a$,b)
Print Part $(3,48),a$,$("$#,###.00"),$(3,30),b
Print
End Sub
}
Currency_Task
- 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 From Optional Item price quantity value hamburger $5.50 4,000,000,000,000,000 $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
Maple
Digits := 50;
tax := .0765;
burgersquantity := 4000000000000000;
burgersprice := 5.50;
burgerscost := burgersquantity * burgersprice;
milkshakesquantity := 2;
milkshakesprice := 2.86;
milkshakescost := milkshakesquantity * milkshakesprice;
total := burgerscost + milkshakescost;
printf("%.2f\n",total);
totaltax := total * tax;
printf("%.2f\n",totaltax);
totalprice := totaltax + total;
printf("%.2f\n",totalprice);
- Output:
22000000000000005.72 1683000000000000.44 23683000000000006.16
Mathematica / Wolfram Language
total = 4000000000000000 Rationalize[5.50] + 2 Rationalize[2.86];
AccountingForm[N[total, 20], {\[Infinity], 2}]
tax = total Rationalize[0.0765];
AccountingForm[N[tax, 20], {\[Infinity], 2}]
AccountingForm[N[total + tax, 20], {\[Infinity], 2}]
- Output:
22000000000000005.72 1683000000000000.44 23683000000000006.16
Nim
Nim doesn’t provide a standard module to deal with decimal values. There exist some third party modules but we have chosen to use the “bignum” library which provides big integers and big rationals.
import strutils
import bignum
type Currency = Int
#---------------------------------------------------------------------------------------------------
func currency(units, subunits: int): Currency =
## Build a currency from units and subunits.
## Units may be negative. Subunits must be in range 0..99.
if subunits notin 0..99:
raise newException(ValueError, "wrong value for subunits")
result = if units >= 0: newInt(units * 100 + subunits)
else: newInt(subunits * 100 - subunits)
#---------------------------------------------------------------------------------------------------
func currency(value: string): Currency =
## Build a currency from a string.
## Negative values are allowed. At most two digits are allowed for subunits.
const StartingChars = Digits + {'-'}
if value.len == 0 or value[0] notin StartingChars:
raise newException(ValueError, "wrong currency string")
# process sign and units.
var units = newInt(0)
var subunits = 0
let sign = if value[0] == '-': -1 else: 1
var idx = if sign == 1: 0 else: 1
while idx < value.len:
if value[idx] notin Digits: break
units = 10 * units + ord(value[idx]) - ord('0')
inc idx
# Process separator.
if idx <= value.high:
if value[idx] != '.':
raise newException(ValueError, "expected a separator")
inc idx
# Process subunits.
for _ in 0..1:
let c = if idx >= value.len: '0' else: value[idx]
if c notin Digits:
raise newException(ValueError, "wrong value for subunits")
subunits = 10 * subunits + ord(c) - ord('0')
inc idx
if idx <= value.high:
raise newException(ValueError, "extra characters after subunits digits")
result = sign * (units * 100 + subunits)
#---------------------------------------------------------------------------------------------------
func `//`(a, b: int): Rat =
## Create a rational value.
newRat(a, b)
#---------------------------------------------------------------------------------------------------
func percentage(a: Currency; p: Rat): Currency =
## Compute a percentage on currency value "a".
## Returned value is rounded to nearest integer.
(a * p.num * 10 div p.denom + 5) div 10
#---------------------------------------------------------------------------------------------------
func `$`(a: Currency): string =
## Build a string representation of a currency value.
result = bignum.`$`(a div 100) & '.' & ($(a mod 100).toInt).align(2, '0')
#———————————————————————————————————————————————————————————————————————————————————————————————————
let hamburgers = currency(5, 50) * int 4_000_000_000_000_000
let milkshakes = currency("2.86") * 2
let rate = 765 // 10_000
let beforeTax = hamburgers + milkshakes
let tax = beforeTax.percentage(rate)
let total = beforeTax + tax
# Find the maximum length of numerical value representations.
let beforeTaxStr = $beforeTax
let taxStr = $tax
let totalStr = $total
let length = max([beforeTaxStr.len, taxStr.len, totalStr.len])
# Display the results.
echo "Total price before tax: ", beforeTaxStr.align(length)
echo "Tax: ", taxStr.align(length)
echo "Total with tax: ", totalStr.align(length)
- Output:
Total price before tax: 22000000000000005.72 Tax: 1683000000000000.44 Total with tax: 23683000000000006.16
OCaml
Using the decimal library.
let () =
let open Decimal in (* bring all functions and operators into scope locally *)
let s = of_string in
let i = of_int in
let hamburgers = s "4e15" * s "5.50" in
let milkshakes = i 2 * s "2.86" in
let tax_rate = s "7.65e-2" in
let subtotal = hamburgers + milkshakes in
let tax = subtotal * tax_rate in
let total = subtotal + tax in
Printf.printf
"Subtotal: %20s
Tax: %20s
Total: %20s\n"
(to_string (round ~n:2 subtotal))
(to_string (round ~n:2 tax))
(to_string (round ~n:2 total))
- Output:
Subtotal: 22000000000000005.72 Tax: 1683000000000000.44 Total: 23683000000000006.16
Perl
use Math::Decimal qw(dec_canonise dec_add dec_mul dec_rndiv_and_rem);
@check = (
[<Hamburger 5.50 4000000000000000>],
[<Milkshake 2.86 2>]
);
my $fmt = "%-10s %8s %18s %22s\n";
printf $fmt, <Item Price Quantity Extension>;
my $subtotal = dec_canonise(0);
for $line (@check) {
($item,$price,$quant) = @$line;
$dp = dec_canonise($price); $dq = dec_canonise($quant);
my $extension = dec_mul($dp,$dq);
$subtotal = dec_add($subtotal, $extension);
printf $fmt, $item, $price, $quant, rnd($extension);
}
my $rate = dec_canonise(0.0765);
my $tax = dec_mul($subtotal,$rate);
my $total = dec_add($subtotal,$tax);
printf $fmt, '', '', '', '-----------------';
printf $fmt, '', '', 'Subtotal ', rnd($subtotal);
printf $fmt, '', '', 'Tax ', rnd($tax);
printf $fmt, '', '', 'Total ', rnd($total);
sub rnd {
($q, $r) = dec_rndiv_and_rem("FLR", @_[0], 1);
$q . substr((sprintf "%.2f", $r), 1, 3);
}
- 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
Phix
requires("1.0.4") -- (mpfr_get_fixed() busted in 1.0.2|3) include mpfr.e mpfr_set_default_precision(-20) -- ensure accuracy to at least 20 d.p. mpfr total_price = mpfr_init("4000000000000000"), tmp = mpfr_init("5.5"), tax = mpfr_init("0.0765"), total = mpfr_init() mpfr_mul(total_price,total_price,tmp) mpfr_set_str(tmp,"2.86") mpfr_mul_si(tmp,tmp,2) mpfr_add(total_price,total_price,tmp) mpfr_mul(tax,total_price,tax) mpfr_add(total,total_price,tax) printf(1,"Total before tax:%26s\n",{mpfr_get_fixed(total_price,2,comma_fill:=true)}) printf(1," Tax:%26s\n",{mpfr_get_fixed(tax,2,comma_fill:=true)}) printf(1," Total:%26s\n",{mpfr_get_fixed(total,2,comma_fill:=true)})
- Output:
Total before tax: 22,000,000,000,000,005.72 Tax: 1,683,000,000,000,000.44 Total: 23,683,000,000,000,006.16
64 bit
As it happens, 64-bit Phix uses 80-bit floats for atoms, which actually have just enough accuracy for this task:
requires(64) atom total_price = 4000000000000000*5.5+2.86*2, tax = total_price*0.0765, total = total_price+tax printf(1,"Total before tax:%,26.2f\n",{total_price}) printf(1," Tax:%,26.2f\n",{tax}) printf(1," Total:%,26.2f\n",{total})
with the same output, however (after deleting the first line) under 32-bit (and under p2js) they would incorrectly end in 4.00/0.25/4.00.
PicoLisp
(scl 2)
(let
(Before
(+
(* 4000000000000000 5.50)
(* 2 2.86) )
Tax (*/ Before 7.65 100.00)
Total (+ Before Tax)
Fmt (17 27) )
(tab Fmt "Total before tax:" (format Before *Scl "." ","))
(tab Fmt "Tax:" (format Tax *Scl "." ","))
(tab Fmt "Total:" (format Total *Scl "." ",")) )
- Output:
Total before tax: 22,000,000,000,000,005.72 Tax: 1,683,000,000,000,000.44 Total: 23,683,000,000,000,006.16
Python
This uses Pythons decimal module, (and some copying of names from the Raku 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
Quackery
[ $ "bigrat.qky" loadfile ] now!
[ 100 * n->v ] is dollars ( n --> n/d )
[ n->v v+ ] is cents ( n/d n --> n/d )
[ rot n->v v* ] is cost ( n n/d --> n/d )
[ $->v drop v* 100 n->v v/ ] is tax ( n/d $ --> n/d )
[ 100 n->v v/
2 point$
$ " $" swap join
' [ 2 split nip ] ]do[
dup -3 peek
char . = if done
dup -2 peek
char . = iff
[ char 0 join ]
done
$ ".00" join ] is currency$ ( n/d --> $ )
[ currency$ echo$ ] is echocurrency ( n/d --> )
4000000000000000 5 dollars 50 cents cost
2 2 dollars 86 cents cost v+
say "Total price before tax: " 2dup echocurrency cr
2dup $ "7.65" tax
say "Tax: " 2dup echocurrency cr
v+
say "Total price with tax: " echocurrency cr
- Output:
Total price before tax: $22000000000000005.72 Tax: $1683000000000000.44 Total price with tax: $23683000000000006.16
Racket
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
Raku
(formerly Perl 6)
No need for a special type in Raku, 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
REXX
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
/*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( '$'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
/*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 /* [↓] commatize number*/
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
Ring
# Project : Currency
nhamburger = "4000000000"
phamburger = "5.50"
nmilkshakes = "2"
pmilkshakes = "2.86"
taxrate = "0.0765"
price = nhamburger * phamburger + nmilkshakes * pmilkshakes
tax = price * taxrate
see "total price before tax : " + price + nl
see "tax thereon @ 7.65 : " + tax + nl
see "total price after tax : " + (price + tax) + nl
Output:
total price before tax : 22000000005.72 tax thereon @ 7.65 : 1683000000.44 total price after tax : 23683000006.16
RPL
Need for big integers.
« →STR IF LASTARG 9 ≤ THEN "0" SWAP + END 1 OVER SIZE 2 - SUB LASTARG NIP 1 + DUP 1 + SUB "$" ROT SIZE LASTARG "0" IFTE + "." + SWAP + » '→CURR' STO « 100 * R→I * DUP →CURR CLLCD 1 DISP .5 WAIT » '→PRICE' STO « DUPDUP FLOOR - EVAL →NUM 0.5 ≥ SWAP CEIL LASTARG FLOOR IFTE » '→RND' STO « 100 * R→I OVER →CURR "TPBT" →TAG UNROT OVER * 100000 / →RND DUP →CURR "Tax" →TAG UNROT + →CURR "TPWT" →TAG » 'TAX→' STO
4000000000000000 5.50 →PRICE 2 2.86 →PRICE + 7.65 TAX→
- Output:
3: TPBT:"$22000000000000005.72" 2: Tax:"$1683000000000000.44" 1: TPWT:"$23683000000000006.16"
Ruby
require 'bigdecimal/util'
before_tax = 4000000000000000 * 5.50.to_d + 2 * 2.86.to_d
tax = (before_tax * 0.0765.to_d).round(2)
total = before_tax + tax
puts "Before tax: $#{before_tax.to_s('F')}
Tax: $#{tax.to_s('F')}
Total: $#{total.to_s('F')}"
- Output:
Before tax: $22000000000000005.72 Tax: $1683000000000000.44 Total: $23683000000000006.16
Rust
extern crate num_bigint; // 0.3.0
extern crate num_rational; // 0.3.0
use num_bigint::BigInt;
use num_rational::BigRational;
use std::ops::{Add, Mul};
use std::fmt;
fn main() {
let hamburger = Currency::new(5.50);
let milkshake = Currency::new(2.86);
let pre_tax = hamburger * 4_000_000_000_000_000 + milkshake * 2;
println!("Price before tax: {}", pre_tax);
let tax = pre_tax.calculate_tax();
println!("Tax: {}", tax);
let post_tax = pre_tax + tax;
println!("Price after tax: {}", post_tax);
}
#[derive(Debug)]
struct Currency {
amount: BigRational,
}
impl Add for Currency {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
amount: self.amount + other.amount,
}
}
}
impl Mul<u64> for Currency {
type Output = Self;
fn mul(self, other: u64) -> Self {
Self {
amount: self.amount * BigInt::from(other),
}
}
}
impl fmt::Display for Currency {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let cents = (&self.amount * BigInt::from(100)).to_integer();
write!(f, "${}.{:0>2}", ¢s / 100, ¢s % 100)
}
}
impl Currency {
fn new(num: f64) -> Self {
Self {
amount: BigRational::new(((num * 100.0).round() as i64).into(), 100.into())
}
}
fn calculate_tax(&self) -> Self {
let tax_val = BigRational::new(765.into(), 100.into());// 7,65 -> 0.0765 after the next line
let amount = (&self.amount * tax_val).ceil() / BigInt::from(100);
Self {
amount
}
}
}
- Output:
Price before tax: $22000000000000005.72 Tax: $1683000000000000.44 Price after tax: $23683000000000006.16
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
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
Smalltalk
Using ScaledDecimal numbers ('sX'-suffix).
check := #(
" amount name price "
(4000000000000000 'hamburger' 5.50s2 )
(2 'milkshakes' 2.86s2 )
).
tax := 7.65s2.
fmt := '%-10s %10P %22P %26P\n'.
totalSum := 0.
totalTax := 0.
Transcript clear.
Transcript printf:fmt withAll:#('Item' 'Price' 'Qty' 'Extension').
Transcript printCR:('-' ,* 72).
check do:[:entry|
|amount name price itemTotal itemTax|
amount := entry[1].
name := entry[2].
price := entry[3].
itemTotal := (price*amount).
itemTax := ((price*amount)*tax/100) roundedToScale.
totalSum := totalSum + itemTotal.
totalTax := totalTax + itemTax.
Transcript printf:fmt
withAll:{name . price . amount . itemTotal}.
].
Transcript printCR:('-' ,* 72).
Transcript printf:fmt withAll:{'' . '' . 'Subtotal' . totalSum}.
Transcript printf:fmt withAll:{'' . '' . 'Tax' . totalTax}.
Transcript printf:fmt withAll:{'' . '' . 'Total' . (totalSum+totalTax)}.
Transcript cr; printCR:('Enjoy your Meal & Thank You for Dining at Milliways')
- Output:
Item Price Qty Extension ------------------------------------------------------------------------ hamburger 5.50 4000000000000000 22000000000000000.00 milkshakes 2.86 2 5.72 ------------------------------------------------------------------------ Subtotal 22000000000000005.72 Tax 1683000000000000.44 Total 23683000000000006.16 Enjoy your Meal & Thank You for Dining at Milliways
Swift
import Foundation
extension Decimal {
func rounded(_ scale: Int, _ roundingMode: NSDecimalNumber.RoundingMode) -> Decimal {
var result = Decimal()
var localCopy = self
NSDecimalRound(&result, &localCopy, scale, roundingMode)
return result
}
}
let costHamburgers = Decimal(4000000000000000) * Decimal(5.50)
let costMilkshakes = Decimal(2) * Decimal(2.86)
let totalBeforeTax = costMilkshakes + costHamburgers
let taxesToBeCollected = (Decimal(string: "0.0765")! * totalBeforeTax).rounded(2, .bankers)
print("Price before tax: $\(totalBeforeTax)")
print("Total tax to be collected: $\(taxesToBeCollected)")
print("Total with taxes: $\(totalBeforeTax + taxesToBeCollected)")
- Output:
Price before tax: $22000000000000005.72 Total tax to be collected: $1683000000000000.44 Total with taxes: $23683000000000006.16
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]"
- Output:
net=22000000000000005.72, tax=1683000000000000.44, total=23683000000000006.16
Unicon
Solution takes advantage of Unicon's FxPt class as well as Unicon's operator overloading extension.
import math
procedure main()
n_burgers := 4000000000000000
n_shakes := 2
price := FxPt(5.50) * n_burgers + FxPt(2.86) * n_shakes
tax := (price * FxPt(7.65/100)).round(2)
total := price + tax
write(left("Price", 10), "$", right(price.toString(),21))
write(left("Tax", 10), "$", right(tax.toString(),21))
write(left("Total", 10), "$", right(total.toString(),21))
end
- Output:
Price $ 22000000000000005.72 Tax $ 1683000000000000.44 Total $ 23683000000000006.16
VBA
Used in locality Euroland. Formatting as currency shows € in stead of $, and thousand separators are "." in stead of "," and the decimail 'point' is "," in stead of "." When run in the USA it will be with dollar sign and so on.
Public Sub currency_task()
'4000000000000000 hamburgers at $5.50 each
Dim number_of_hamburgers As Variant
number_of_hamburgers = CDec(4E+15)
Dim price_of_hamburgers As Currency
price_of_hamburgers = 5.5
'2 milkshakes at $2.86 each, and
Dim number_of_milkshakes As Integer
number_of_milkshakes = 2
Dim price_of_milkshakes As Currency
price_of_milkshakes = 2.86
'a tax rate of 7.65%.
Dim tax_rate As Single
tax_rate = 0.0765
'the total price before tax
Dim total_price_before_tax As Variant
total_price_before_tax = number_of_hamburgers * price_of_hamburgers
total_price_before_tax = total_price_before_tax + number_of_milkshakes * price_of_milkshakes
Debug.Print "Total price before tax "; Format(total_price_before_tax, "Currency")
'the tax
Dim tax As Variant
tax = total_price_before_tax * tax_rate
Debug.Print "Tax "; Format(tax, "Currency")
'the total with tax
Debug.Print "Total with tax "; Format(total_price_before_tax + tax, "Currency")
End Sub
- Output:
Total price 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
Wren
import "./big" for BigRat
var hamburgers = BigRat.new("4000000000000000")
var milkshakes = BigRat.two
var price1 = BigRat.fromFloat(5.5)
var price2 = BigRat.fromFloat(2.86)
var taxPc = BigRat.fromFloat(0.0765)
var totalPc = BigRat.fromFloat(1.0765)
var totalPreTax = hamburgers*price1 + milkshakes*price2
var totalTax = taxPc * totalPreTax
var totalAfterTax = totalPreTax + totalTax
System.print("Total price before tax : %((totalPreTax).toDecimal(2))")
System.print("Tax : %((totalTax).toDecimal(2))")
System.print("Total price after tax : %((totalAfterTax).toDecimal(2))")
- Output:
Total price before tax : 22000000000000005.72 Tax : 1683000000000000.44 Total price after tax : 23683000000000006.16
zkl
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.
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
- Programming Tasks
- Solutions by Programming Task
- 11l
- Ada
- ALGOL 68
- AppleScript
- AWK
- BBC BASIC
- Bracmat
- C
- GMP
- C sharp
- Clojure
- Clojurewerkz/money
- Io.randomseed/bankster
- COBOL
- Common Lisp
- Delphi
- System.SysUtils
- Velthuis.BigRationals
- Velthuis.BigDecimals
- Velthuis.BigIntegers
- F Sharp
- Factor
- FreeBASIC
- Frink
- FutureBasic
- Go
- Haskell
- J
- Java
- JavaScript
- Jq
- Julia
- Kotlin
- Lua
- M2000 Interpreter
- Maple
- Mathematica
- Wolfram Language
- Nim
- Bignum
- OCaml
- Perl
- Phix
- Phix/mpfr
- PicoLisp
- Python
- Quackery
- Racket
- Raku
- REXX
- Ring
- RPL
- Ruby
- Rust
- Scala
- Sidef
- Smalltalk
- Swift
- Tcl
- Tcllib
- Unicon
- VBA
- Wren
- Wren-big
- Zkl