Front page | perl.perl5.porters |
Postings from September 2009
Proposal: perl with financial values
Thread Next
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
Thread Next
-
Proposal: perl with financial values
by Klaus