Fun With Numbers

Discussion in 'Javascript' started by Gene Wirchenko, Feb 3, 2012.

1. Gene WirchenkoGuest

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

Gene Wirchenko, Feb 3, 2012

2. Tom de NeefGuest

"Gene Wirchenko" <>
> 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

Tom de Neef, Feb 3, 2012

3. Gene WirchenkoGuest

On Fri, 3 Feb 2012 10:29:56 +0100, "Tom de Neef" <>
wrote:

>"Gene Wirchenko" <>
>> 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 ?

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

Gene Wirchenko, Feb 3, 2012
4. Evertjan.Guest

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

>>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 ?

>

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,

--
Evertjan.
The Netherlands.

Evertjan., Feb 3, 2012
5. Thomas 'PointedEars' LahnGuest

Tom de Neef wrote:

> "Gene Wirchenko" <>
>> 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 ?

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
--
When all you know is jQuery, every problem looks \$(olvable).

Thomas 'PointedEars' Lahn, Feb 4, 2012
6. Thomas 'PointedEars' LahnGuest

Tom de Neef wrote:

> "Gene Wirchenko" <>
>> 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 ?

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
--
When all you know is jQuery, every problem looks \$(olvable).

Thomas 'PointedEars' Lahn, Feb 4, 2012
7. Thomas 'PointedEars' LahnGuest

Tom de Neef wrote:

> "Gene Wirchenko" <>
>> 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 ?

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
--
When all you know is jQuery, every problem looks \$(olvable).

Thomas 'PointedEars' Lahn, Feb 4, 2012
8. Dr J R StocktonGuest

In comp.lang.javascript message <v3rmi7l1i7ji615ltsn7mncfsh2tf6ip2k@4ax.
com>, Thu, 2 Feb 2012 21:23:43, Gene Wirchenko <> posted:

> 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
-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

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.

--
(c) John Stockton, nr London UK ?@merlyn.demon.co.uk IE8 FF8 Op11 Sf5 Cr15
news:comp.lang.javascript FAQ <http://www.jibbering.com/faq/index.html>.
<http://www.merlyn.demon.co.uk/js-index.htm> jscr maths, dates, sources.