Front page | perl.perl5.porters | Postings from September 2009

## Proposal: perl with financial values

From:
Klaus
Date:
September 3, 2009 09:36
Subject:
Proposal: perl with financial values
Message ID:
1ffb04f20909030659j6228713cy3f1bac17f48f1956@mail.gmail.com
```First of all, let me state that my XS- and C-programming skills are not very
good, so I would not be able to program what I am about to propose. But I
would like to ask p5p if my thoughts make any sense.

Here is my idea:

I propose a new lexically scoped pragma 'use financial;' (and its
counter-part 'no financial;') to replace the usual floating point
arithmetric by decimal numbers which allows for accurate arithmetic in
financial perl applications.

My idea is a replacement for the existing "NV"-value by a new "financial"
value (what I would call "YV"-value), which consists of a signed 64-bit
mantissa (even on 32-bit perls) and a signed 8-bit exponent. The mantissa is
a normal integer in the range (2**63 - 1) .. (-2**63), the exponent is in
the range (127 .. -128).

The important point here is that the exponent represents a power of 10 (and
not of 2), which allows for accurate representations of numbers such as
0.01, 14.7, 0.0597, etc... (i.e.: 0.01 ==> 1 * 10**(-2), 14.7 = 147 *
10**(-1), 0.0597 = 597 * 10**(-4)), that don't have accurate representations
in binary form.

"Simple" arithmetic between "YV"-values (addition, substraction,
multiplication and exponentiation with positive integer exponents) would be
implemented as C-code:

Addition/Subtraction of two "YV"-values ==> normalize the two exponents to
the lower of the two, then perform addition/substraction of the two
mantissas (on 32-bit perls, the mantissa still would be 64-bit long,
therefore the addition would have to be performed in two steps).
Normalisation: if the resulting mantissa is a power of 10 itself, then
divide the mantissa by that power of 10 and increase the exponent
accordingly.

Multiplication and exponentiation with positive integer exponents of "YV"
values ==> multiply the two 64-bit mantissas into a 128-bit temp-variable,
then add the two exponents. Normalisation: if the resulting mantissa is a
power of 10 itself, then divide the mantissa by that power of 10 and
increase the exponent accordingly. Final adjustment: if the 128-bit result
is greater than 2**63, then divide mantissa by 10 (and add 1 to exponent)
until the 128-bit mantissa falls below 2**63.

More complicated arithmetic between "YV"-values (division, exponentiation
with a negative or non-integer exponent, square-root, sine, cosine, log, etc
...) would be perfomed by first converting the "YV"-values into temporary
"NV"-values, then performing the traditional arithmetic, and finally
converting the result back to a "YV"-value.

Converting from traditional "NV" values to the new "YV" values would be
performed with a C-algorithm that is the equivalent of the already existing
function sprintf("%.128f", ...) and converting from the new "YV"-values back
to traditional "NV"-values would be performed with a C-algorithm that is the
equivalent of the already existing function that converts strings
("PV"-values) into "NV"-values.

Arithmetic with "YV" values would be slower than the usual "double"
arithmetic, but still sufficiently fast, because "financial" arithmetic is
implemented as efficient C-code.

When we encounter "YV"-values, mixed with traditional "NV"-values, the
following rules apply:

Inside a lexically scoped "use financial;" ==> we convert the "NV"-value
into a temporary "YV"-variable and perform the "YV"-arithmetic with the
temporary variable. All numerical constants inside a lexically scoped "use
financial;" are either integers ("IV") or "YV"-values.

Outside a lexically scoped "use financial;" ==> we convert the "YV"-value
into a temporary "NV"-variable and perform the "NV"-arithmetic with the
temporary variable. All numerical constants outside a lexically scoped "use
financial;" are either integers ("IV") or "NV"-values.

Here is a simple test (which, of course, fails, because "use financial;" is
not yet implemented):

=====================================================
use strict;
use warnings;

use Test::More tests => 2;
{
use financial;
my \$z = 0;
for (1..100) { \$z += 0.01; }

ok(\$z == 1, 'use financial --> 0.01 * 100 == 1');
}

{
no financial;
my \$z = 0;
for (1..100) { \$z += 0.01; }
ok(\$z != 1, 'no financial --> 0.01 * 100 != 1');
}
=====================================================
--
klaus03@gmail.com

```