Fun With Numbers

G

Gene Wirchenko

Dear JavaScripters:

I need to do work with fixed-decimal quantities (mainly dollar
amounts but others as well). I need to be able to do reliable
arithmetic with them.

0.1 + 0.1 + 0.1 equals 0.15 + 0.15 mathematically, but not with
floating point. I need to have it equal.

So I am cheating. I am storing fixed-decimal amounts internally
as integers. When I need to output a value, I scale it, but any
arithmetic or comparison operations will be on the integers.

My first thought was that I was safe for nine digits worth,
because ECMAScript does a lot of 32-bit operations. That is on the
edge of what I need. The non-JavaScript system that I maintain now
has reports that generate nine digits worth in some reports.

I did some experimenting, and it appears that I might be able to
get 15 digits of precision, but not 16. After determining this, I
referred to the ECMAScript standard (ECMA-262 5.1 Edition of June
2011) to see if this matched. That standard says that it uses the
IEEE 754 floating point format, and some documentation on that says
that it is good for 15.95 digits of precision.

So far, so good.

But am I safe?

Can I count on exact arithmetic with integers of up to 15 digits
of precision?

If yes, can you please point to a reference? If no, please give
me a counterexample.

I would rather not have to mess around like this, but one of
JavaScript's nasty bits is only one number type.

Sincerely,

Gene Wirchenko
 
T

Tom de Neef

Gene Wirchenko said:
I need to do work with fixed-decimal quantities (mainly dollar
amounts but others as well). I need to be able to do reliable
arithmetic with them.

0.1 + 0.1 + 0.1 equals 0.15 + 0.15 mathematically, but not with
floating point. I need to have it equal.

So I am cheating. I am storing fixed-decimal amounts internally
as integers. When I need to output a value, I scale it, but any
arithmetic or comparison operations will be on the integers.

Just wondering: how do you calculate 3,62% of ($4.56 + $14.13) and equal it
to 3,62% of $4.56 plus 3,62% of $14.13 ?
Or go from euro to dollar, etc ?
Tom
 
G

Gene Wirchenko

Just wondering: how do you calculate 3,62% of ($4.56 + $14.13) and equal it
to 3,62% of $4.56 plus 3,62% of $14.13 ?
Or go from euro to dollar, etc ?

I have so far tested with addition, but multiplication is only a
bit more complicated.

3.62% = 0.0362 requires a precision of (4,4) [meaning total
digits and decimal digits]. The dollar amounts are (3,2) and (4,2).
Whenever a dollar amount and a percentage are multiplied, the result
requires 6 decimal places. When it matters for it to be converted to
a dollar value, I will round and rescale the value.
$4.56 + $14.13 = $18.69 then * 0.0362 = $0.676578
0.0362 * $4.56 = $0.165072
0.0362 * $14.13 = $0.511506
sum: $0.676578
So the amounts are equal. I would round after completing all
operations and rescale to get $0.68. (If you round partway through,
yes, you can get rounding errors.)

Internally, the above is done with integers with the parened
numbers indicating the number of decimal digits:
$456(2) + $1413(2) = $1869(2) then * 362(4) = $676578(6)
362(4) * $456(2) = $165072(6)
362(4) * $1413(2) = $511506(6)
sum: $676578(6)
Round and scale to $68(2) which is $0.68.

Sincerely,

Gene Wirchenko
 
E

Evertjan.

Gene Wirchenko wrote on 03 feb 2012 in comp.lang.javascript:

integer multiplication [20 items sole at ...] of currency needs to be
exact, so do this multiplication on integer cents.
And do not convert from and to binary floating point dollars at all,
use strings.

Percentage or currency conversion needs to be rounded anyway,
so here the problem of binary math does not really exist.

============

We used to have Basic implementations with BNC [Binary Coded Decimal]-math,
those where the days!

I wrote here 20 Jan 2006:
"Central Data Basic" for the Signetics 2650 microprocessor,
rumored to be coded by a William Gates in the early 1980's,
had excellent BCD support.

Nice to read that threaad again with those familiar names:
<http://bytes.com/topic/javascript/answers/447636-show-hex-numbers>
 
T

Thomas 'PointedEars' Lahn

Tom said:
Just wondering: how do you calculate 3,62% of ($4.56 + $14.13) and equal
it to 3,62% of $4.56 plus 3,62% of $14.13 ?
Or go from euro to dollar, etc ?

You store the floating-point number n as an integer value (i.e., this.value
% 1 = 0) and store the number of significant decimal digits of n
(this.scale) along with it in an object. Then

n = this.value × 10^(−this.scale).

Of course, in order to do basic arithmetic with the object, you need special
methods that account for the scale. That is,

a.b + c.de
~ ab:scale1 + cde:scale2
~ (ab × 10^(scale2 − scale1) + cde)):max(scale1, scale2)
~ (ab × 10^(scale2 − scale1) + cde) × 10^(−max(scale1, scale2)),

and

a.b + c.de
~ ab:scale1 × cde:scale2
~ (ab × 10^(scale2 − scale1) × cde):(scale1 × scale2)
~ (ab × 10^(scale2 − scale1) × cde) × 10^(−scale1 × scale2)

where scale1 <= scale2 (here: scale1 = 1, scale2 = 2).

The idea is anything but new. For example, Java has had this for many
years:
<http://docs.oracle.com/javase/1.5.0/docs/api/java/math/BigDecimal.html>

The precision of an implementation of this in an ECMAScript implementation
is limited by the precision for integer values (number values `i' with i % 1
== 0), as the unscaled value and the scale are still IEEE-754 floating-point
values. But the precision of computation is indeed better than with plain
floating-point values, that is 1:1 + 1:1 + 1:1 ~ 3:1 ~ 0.3.


PointedEars
 
T

Thomas 'PointedEars' Lahn

Tom said:
Just wondering: how do you calculate 3,62% of ($4.56 + $14.13) and equal
it to 3,62% of $4.56 plus 3,62% of $14.13 ?
Or go from euro to dollar, etc ?

You store the floating-point number n as an integer value (i.e., this.value
% 1 = 0) and store the number of significant decimal digits of n
(this.scale) along with it in an object. Then

n = this.value × 10^(−this.scale).

Of course, in order to do basic arithmetic with the object, you need special
methods that account for the scale. That is,

a.b × c.de
~ ab:scale1 + cde:scale2
~ (ab × 10^(scale2 − scale1) + cde)):max(scale1, scale2)
~ (ab × 10^(scale2 − scale1) + cde) × 10^(−max(scale1, scale2)),

and

a.b + c.de
~ ab:scale1 × cde:scale2
~ (ab × 10^(scale2 − scale1) × cde):(scale1 × scale2)
~ (ab × 10^(scale2 − scale1) × cde) × 10^(−scale1 × scale2)

where scale1 <= scale2 (here: scale1 = 1, scale2 = 2).

The idea is anything but new. For example, Java has had this for many
years:
<http://docs.oracle.com/javase/1.5.0/docs/api/java/math/BigDecimal.html>

The precision of an implementation of this in an ECMAScript implementation
is limited by the precision for integer values (number values `i' with i % 1
== 0), as the unscaled value and the scale are still IEEE-754 floating-point
values. But the precision of computation is indeed better than with plain
floating-point values, that is 1:1 + 1:1 + 1:1 ~ 3:1 ~ 0.3.


PointedEars
 
T

Thomas 'PointedEars' Lahn

Tom said:
Just wondering: how do you calculate 3,62% of ($4.56 + $14.13) and equal
it to 3,62% of $4.56 plus 3,62% of $14.13 ?
Or go from euro to dollar, etc ?

You store the floating-point number n as an integer value (i.e., this.value
% 1 = 0) and store the number of significant decimal digits of n
(this.scale) along with it in an object. Then

n = this.value × 10^(−this.scale).

Of course, in order to do basic arithmetic with the object, you need special
methods that account for the scale. That is,

a.b + c.de
~ ab:scale1 + cde:scale2
~ (ab × 10^(scale2 − scale1) + cde)):max(scale1, scale2)
~ (ab × 10^(scale2 − scale1) + cde) × 10^(−max(scale1, scale2)),

and

a.b × c.de
~ ab:scale1 × cde:scale2
~ (ab × 10^(scale2 − scale1) × cde):(scale1 × scale2)
~ (ab × 10^(scale2 − scale1) × cde) × 10^(−scale1 × scale2)

where scale1 <= scale2 (here: scale1 = 1, scale2 = 2).

The idea is anything but new. For example, Java has had this for many
years:
<http://docs.oracle.com/javase/1.5.0/docs/api/java/math/BigDecimal.html>

The precision of an implementation of this in an ECMAScript implementation
is limited by the precision for integer values (number values `i' with i % 1
== 0), as the unscaled value and the scale are still IEEE-754 floating-point
values. But the precision of computation is indeed better than with plain
floating-point values, that is 1:1 + 1:1 + 1:1 ~ 3:1 ~ 0.3.


PointedEars
 
D

Dr J R Stockton

In comp.lang.javascript message <v3rmi7l1i7ji615ltsn7mncfsh2tf6ip2k@4ax.
I need to do work with fixed-decimal quantities (mainly dollar
amounts but others as well). I need to be able to do reliable
arithmetic with them.

Dollars are integers. No problem, up to and including 2^53.
0.1 + 0.1 + 0.1 equals 0.15 + 0.15 mathematically, but not with
floating point. I need to have it equal.

You only get assured exact results if all numbers, including
intermediates, can be expressed in fixed-point binary with the most
significant and least significant bits no more than about 53 buts apart.

But the PC FPU, and maybe others, can work with 64-bit mantissa numbers,
so "internal" intermediates could be more precise - which might or might
not be standards-compliant.

Aside : we should be able to think of a test for that/

If you want to do exact addition, subtraction, multiplication,
comparison with dollar-cent prices, work in cents.

So I am cheating. I am storing fixed-decimal amounts internally
as integers. When I need to output a value, I scale it, but any
arithmetic or comparison operations will be on the integers.

My first thought was that I was safe for nine digits worth,
because ECMAScript does a lot of 32-bit operations. That is on the
edge of what I need. The non-JavaScript system that I maintain now
has reports that generate nine digits worth in some reports.

The 32-bit operations are logical, not arithmetic. They are not needed
in calculating finance.
I did some experimenting, and it appears that I might be able to
get 15 digits of precision, but not 16. After determining this, I
referred to the ECMAScript standard (ECMA-262 5.1 Edition of June
2011) to see if this matched. That standard says that it uses the
IEEE 754 floating point format, and some documentation on that says
that it is good for 15.95 digits of precision.

Not entirely so. A single non-integer addition operation is good to
about that, depending on by how much the answer is below the nearest
power of two above. But subtraction of two inexactly-represented
quantities of similar magnitude loses precision.

Have you had the lesson on how to code the solutions of a potentially
ill-conditioned quadratic equation, concerning
-b +- root(b^2-4ac) / 2a // ???
But am I safe?

Can I count on exact arithmetic with integers of up to 15 digits
of precision?

Only if you really understand what you are doing. And not always then,
since most arithmetic expressions involving division have ideal results
which cannot be exactly represented in an IEEE Double.

And if you're not using a P60's FPU.
If yes, can you please point to a reference? If no, please give
me a counterexample.

You could read my Web site, to reduce the frequency with which you
"discover" quite well-known wheels.


If it is necessary to reproduce the results which would be obtained by a
professional accountant using pre-computer equipment, then you are only
safe if you follow his methods exactly. That does not mean getting the
right answer, it means getting the same answer.


Happily, modern computers are so fast, and accounting is so simple, that
you^H^H^Hone can write an exact arbitrary-length decimal arithmetic
package without too much difficulty.
Via <http://www.merlyn.demon.co.uk/programs/00index.htm longcalc, for
example.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,997
Messages
2,570,241
Members
46,832
Latest member
UtaHetrick

Latest Threads

Top