NAME
Math::Decimal64 - perl interface to C's _Decimal64 operations.
DEPENDENCIES
In order to compile this module, a C compiler that provides
the _Decimal64 type is needed.
DESCRIPTION
Math::Decimal64 supports up to 16 decimal digits of significand
(mantissa) and an exponent range of -383 to +384.
The smallest expressable value is -9.999999999999999e384 (which
is also equivalent to -9999999999999999e369).
The largest expressable value is 9.999999999999999e384 (which
also equivalent to 9999999999999999e369).
The closest we can get to zero is (plus or minus) 1e-384
(which is also equivalent to 1000000000000000e-399).
This module allows decimal floating point arithmetic via
operator overloading - see
"OVERLOADING"
.
In the documentation that follows,
"$mantissa"
is a perl
scalar
holding a string of up to 16 decimal digits, optionally prefixed
with
a
'+'
or
'-'
sign:
$mantissa
=
'1234'
;
$mantissa
=
'1234567890123456'
;
SYNOPSIS
my
$d64_1
= MEtoD64(
'9927'
, -2);
# the decimal 99.27
my
$d64_2
= MEtoD64(
'3'
, 0);
# the decimal 3.0
$d64_1
/=
$d64_2
;
$d64_1
;
# prints 3309e-2 (33.09)
OVERLOADING
The following operations are overloaded:
+ - * /
+= -= *= /=
!= == <= >= <=> < >
++ --
""
abs
bool
int
neg
=
you understand its caveats. See
'perldoc overload'
and
read
it thoroughly, including the documentation regarding
'copy constructors'
.
Arguments to the overloaded operations must be Math::Decimal64
objects, integer (IV/UV)
values
or string (PV)
values
.
Strings can match /^(\-|\+)?(nan|inf)/i or be in floating point,
scientific notation or integer formats. Eg
'113'
,
'12.34'
,
'12e-9'
,
'-12.34e+106'
,
'-9E8'
,
'-NaN'
,
'inf'
are all valid strings.
$d64_2
=
$d64_1
+
$d128_0
;
#ok
$d64_2
=
$d64_1
+ 15;
#ok
$d64_2
=
$d64_1
+ 3.1;
# Error
If you really want to add the NV 3.1 you need to:
$d64_2
=
$d64_1
+ NVtoD64(3.1);
If you instead wish to add the decimal value 3.1:
$d64_2
=
$d64_1
+
'3.1'
;
Overloading of floats (NV
values
) will probably never be enabled
as that would make it very easy to inadvertently introduce a value
that was not intended.
CREATION & ASSIGNMENT FUNCTIONS
The following create and assign a new Math::Decimal64 object.
##################################
# Create, and assign from a string
$d64
= PVtoD64(
$string
);
eg:
$d64
= PVtoD64(
'-9427199254740993'
);
$d64
= PVtoD64(
'-9307199254740993e-15'
);
$d64
= Math::Decimal64->new(
'-9787199254740.993e-20'
);
$d64
= Math::Decimal64->new(
'-9307199254740993e-23'
);
$d64
= Math::Decimal64->new(
'-inf'
);
$d64
= Math::Decimal64->new(
'nan'
);
If the string arg contains characters that (according to perl's
looks_like_number API function) don't make sense in numeric
context, then a global non-numeric flag which was initialised to
0 is incremented - and the value assigned is in accordance
with
perl's usual rules. If
$Math::Decimal64::NNW
(set to 0 by
default
)
is set to 1, then a non-numeric warning is also issued whenever
the non-numeric flag is incremented. The arg can be in either
integer
format
, scientific notation, float
format
or (+-)inf/nan.
Doing Math::Decimal64->new(
$string
) will also create and assign
using PVtoD64().
The nnumflag function returns the current value of the global.
It can be cleared to 0 by running clear_nnum() and set to x
with
set_nnum(x).
PVtoD64 is now a much improved way of creating and assigning - so
much so that I'm now recommending it as the preferred way of
creating a Math::Decimal64 object.
If you have a (
$mantissa
,
$exponent
) pair as your value and you
wish to create a Math::Decimal64 object using PVtoD64 you can
do
:
$d64
= PVtoD64(MEtoPV(
$mantissa
,
$exponent
));
or simply:
$d64
= PVtoD64(
$mantissa
.
'e'
.
$exponent
);
###############################################
# Create, and assign from mantissa and exponent
$d64
= MEtoD64(
$mantissa
,
$exponent
);
eg:
$d64
= MEtoD64(
'12345'
, -3);
# 12.345
It's a little kludgy, but this is a safe and sure way
of creating the Math::Decimal64 object
with
the intended
value.
Checks are conducted to ensure that the arguments are suitable.
The mantissa string must represent an integer. (There's an
implicit
'.0'
at the end of the string.)
Doing Math::Decimal64->new(
$mantissa
,
$exponent
) will also
create and assign using MEtoD64(), and is equally acceptable.
###############################################
# Create, and assign from mantissa and exponent
$d64
= DPDtoD64(
$mantissa
,
$exponent
);
eg:
$d64
= DPDtoD64(
'12345'
, -3);
# 12.345
This is perhaps a quicker way of creating the Math::Decimal64
object
with
the intended value - but works only
for
DPD
format
- ie only
if
d64_fmt() returns
'DPD'
.
The mantissa string can be
'inf'
or
'nan'
, optionally prefixed
represent an integer value (
with
implied
'.0'
at the end) - ie
cannot contain a decimal point.
#################################################
# Create, and assign from a UV (unsigned integer)
$d64
= UVtoD64(
$uv
);
eg:
$d64
= UVtoD64(~0);
Doing Math::Decimal64->new(
$uv
) will also create and assign
using UVtoD64().
Assigns the designated UV value to the Math::Decimal64 object
(but loses precision
if
the _Decimal64 type
has
insufficient
precision to accommodate the precision of the IV).
################################################
# Create, and assign from an IV (signed integer)
$d64
= IVtoD64(
$iv
);
eg:
$d64
= IVtoD64(-15);
# -15.0
Doing Math::Decimal64->new(
$iv
) will also create and assign
using IVtoD64().
Assigns the designated IV value to the Math::Decimal64 object
(but loses precision
if
the _Decimal64 type
has
insufficient
precision to accommodate the precision of the IV).
############################################################
# Create, and assign from an existing Math::Decimal64 object
$d64
= D64toD64(
$d64_0
);
Also:
$d64
= Math::Decimal64->new(
$d64_0
);
$d64
=
$d64_0
;
# uses overloaded '='
#######################################
# Create, and assign from an NV (real))
$d64
= NVtoD64(
$nv
);
eg:
$d64
= NVtoD64(-3.25);
Doing Math::Decimal64->new(
$nv
) is now a fatal error. NV's can
now be assigned using only either NVtoD64() or assignNV().
Might not always assign the value you think it does, but should
be fine
for
assigning decimal
values
that have en exact base 2
representation. (Eg, see test 5 in t/overload_cmp.t.)
################################
# Create, and assign using new()
$d64
= Math::Decimal64->new([
$arg1
, [
$arg2
]]);
This function calls one of the above functions. It
determines the appropriate function to call by examining
the argument(s) provided.
If
no
argument is provided, a Math::Decimal64 object
with
a value of NaN is returned.
If 2 arguments are supplied it uses MEtoD64().
If one argument is provided, that arg's internal flags are
used to determine the appropriate function to call.
Dies
if
that argument is an NV - allowing an NV argument makes
it very easy to inadvertently assign an unintended value, and
is therefore now disallowed.
###################################
# Create, and assign using STRtoD64
$d64
= STRtoD64(
$string
);
If your C compiler provides the strtod64 function, and
you configured the Makefile.PL to enable access to that
Usage is is as
for
PVtoD64().
##############################
ASSIGN A NEW VALUE TO AN EXISTING OBJECT
#####################################
assignME(
$d64
,
$mantissa
,
$exponent
);
Assigns the value represented by (
$mantissa
,
$exponent
)
to the Math::Decimal64 object,
$d64
.
Performs same argument checking as MEtoD64.
eg: assignME(
$d64
,
'123459'
, -6);
# 0.123459
######################################
assignDPD(
$d64
,
$mantissa
,
$exponent
);
Assigns the value represented by (
$mantissa
,
$exponent
)
to the Math::Decimal64 object,
$d64
. This might work
more efficiently than assignME(), but works only
when
the
_Decimal64type is DPD-formatted. (The d64_fmt function
will
tell
you whether the _Decimal64 is DPD-formatted or
BID-formatted.)
eg: assignDPD(
$d64
,
'123459'
, -6);
# 0.123459
########################
assignIV (
$d64
,
$iv
);
assignUV (
$d64
,
$uv
);
assignNV (
$d64
,
$nv
);
assignPV (
$d64
,
$string
);
# See PVtoD64 doc (above)
assignD64(
$d64
,
$d64_0
);
Assigns the value represented by the second arg (resp. the
IV,UV,NV,PV, Math::Decimal64 object) to the
Math::Decimal64 object,
$d64
.
eg: assignPV(
$d64
,
'12.3459e-6'
);
# 0.0000123459
################
assignNaN(
$d64
);
Assigns a NaN to the Math::Decimal64 object,
$d64
.
#######################
assignInf(
$d64
,
$sign
);
Assigns an Inf to the Math::Decimal64 object,
$d64
.
If
$sign
is negative, assigns -Inf; otherwise +Inf.
#######################
INF, NAN and ZERO OBJECTS
#####################
$d64
= InfD64(
$sign
);
If
$sign
< 0, creates a new Math::Decimal64 object set to
negative infinity;
else
creates a Math::Decimal64 object set
to positive infinity.
################
$d64
= NaND64();
Creates a new Math::Decimal64 object set to NaN.
Same as
"$d64 = Math::Decimal64->new();"
######################
$d64
= ZeroD64(
$sign
);
If
$sign
< 0, creates a new Math::Decimal64 object set to
negative zero;
else
creates a Math::Decimal64 object set to
zero.
#######################
RETRIEVAL FUNCTIONS
The following functions provide ways of seeing the value of
Math::Decimal64 objects.
###########################
$string
= decode_d64(
$d64
);
This function calls either decode_dpd() or decode_bid(),
depending upon the formatting used to encode the
_Decimal64 value (as determined by the d64_fmt()
sub
).
It returns the value as a string of the form (-)ME, where:
"M"
is the mantissa, containing up to 16 base 10 digits;
"E"
is the letter
"e"
followed by the exponent;
A minus sign is prefixed to any -ve number (incl -0), but
no
sign at all is prefixed
for
+ve numbers (incl +0).
Returns the strings
'+inf'
,
'-inf'
,
'nan'
for
(respectively)
+infinity, -infinity, NaN.
The value will be decoded correctly.
##################################
$string
= decode_dpd(
$d64_binary
);
$string
= decode_bid(
$d64_binary
);
As
for
decode_d64(), except it takes the 64-bit binary
representation of the _Decimal64 value as its argument. This
argument is derived from the Math::Decimal64 object (
$d64
)
by doing:
$binary
= hex2bin(d64_bytes(
$d64
));
DPD and BID formats will
return
different strings - so you
need to know which encoding (DPD or BID) was used, and then
call the appropriate decode_*() function
for
that encoding.
$Math::Decimal64::fmt
will
tell
you which encoding is in
use
,
as also will the d64_fmt() subroutine.
###########################
$fstring
= D64toFSTR(
$d64
);
Returns a string in floating point
format
(as distinct from
scientific notation) - ie as 0.123 instead of 123e-3.
And, yes, (eg) the _Decimal64 value 123e201 will be returned
as a string consisting of
'123'
followed by 201 zeroes.
####################################
$rstring
= D64toRSTR(
$d64
,
$places
);
Same as D64toFSTR() but the returned string
has
been rounded
(to nearest, ties to even) to the number of decimal places
specified by
$places
.
Croaks
with
appropriate error message
if
$places
< 0.
#######################################
(
$mantissa
,
$exponent
) = D64toME(
$d64
);
Returns the value of the Math::Decimal64 object as a
mantissa (string of up to 16 decimal digits) and exponent.
You can then manipulate those
values
to output the
value in your preferred
format
. Afaik, the value will be
decoded accurately.
########################################
(
$mantissa
,
$exponent
) = FR64toME(
$d64
);
Requires that Math::MPFR version 3.18 or later
has
been
loaded. It also requires that Math:MPFR
has
been built
with
support
for
the mpfr library's Decimal64 conversion
functions - in which case Math::MPFR::_WANT_DECIMAL_FLOATS()
will
return
true. (Otherwise it returns false.)
Afaik, the value will be decoded accurately.
####################
$nv
= D64toNV(
$d64
);
This function returns the value of the Math::Decimal64
object to a perl
scalar
(NV). Under certain conditions
it may not translate the value accurately.
###########
$d64
;
Will
the value in the
format
(eg) -12345e-2, which
equates to the decimal -123.45. Uses D64toME().
#########
pFR
$d64
;
Will
the value in the
format
(eg) -12345e-2, which
equates to the decimal -123.45. Uses FR64toME() - which
should always
the value accurately, but requires
that Math::MPFR:
1)
has
been loaded;
2) supports the Decimal64 mpfr conversion functions.
#########
OTHER FUNCTIONS
##################################
$iv
= Math::Decimal64::nnumflag();
# not exported
Returns the value of the non-numeric flag. This flag is
initialized to zero, but incemented by 1 whenever the
_atodecimal function (used internally by assignPV and
PVtoD64) is handed a string containing non-numeric
characters. The value of the flag therefore tells us how
many
times
_atodecimal() was handed such a string. The flag
can be
reset
to 0 by running clear_nnum().
###############################
Math::Decimal64::set_nnum(
$iv
);
# not exported
Resets the global non-numeric flag to the value specified by
$iv
.
##############################
Math::Decimal64::clear_nnum();
# not exported
Resets the global non-numeric flag to 0.(Essentially the same
as running set_nnum(0).)
################################
(
$man
,
$exp
) = PVtoME(
$string
);
$string
is a string representing a floating-point value - eg
'inf'
,
'+nan'
,
'123.456'
,
'-1234.56e-1'
, or
'12345.6E-2'
.
The function returns an array of (mantissa, exponent), where
the mantissa is a string of base 10 digits (prefixed
with
a
'-'
for
-ve
values
)
with
an implied decimal point at the
end of the string. For strings such as
'inf'
and
'nan'
, the
mantissa will be set to
$string
, and the exponent to 0.
For the example strings
given
above, the returned arrays
would be (
'inf'
, 0), (
'+nan'
, 0), (
'123456'
, -3), (
'-123456'
,
-3) and (
'123456'
, -3) respectively.
#######################################
$string
= MEtoPV(
$mantissa
,
$exponent
);
If
$mantissa
=~ /inf|nan/i returns
$mantissa
.
Else returns
$mantissa
.
'e'
.
$exponent
.
#################
$fmt
= d64_fmt();
Returns either
'DPD'
or
'BID'
, depending upon whether the
(internal) _Decimal64
values
are encoded using the 'Densely
Packed Decimal
' format or the '
Binary Integer Decimal'
format
.
#######################
$hex
= d64_bytes(
$d64
);
Returns the
hex
representation of the _Decimal64 value
as a string of 16
hex
characters.
############################
$binary
= hex2bin(
$d64_hex
);
Takes the string returned by d64_bytes (above) and
rewrites it in binary form - ie as a string of 64 base 2
digits.
#################
$d64
= DEC64_MAX;
# 9999999999999999e369
$d64
= DEC64_MIN;
# 1e-398
DEC64_MAX is the largest positive finite representable
_Decimal64 value.
DEC64_MIN is the smallest positive non-zero representable
_Decimal64 value.
Multiply these by -1 to get their negative counterparts.
###################
$d64
= Exp10(
$pow
);
Returns a Math::Decimal64 object
with
a value of
10 **
$pow
,
for
$pow
in the range (-398 .. 384). Croaks
with
appropriate message
if
$pow
is not within that range.
########################
$bool
= have_strtod64();
Returns true
if
,
when
building Math::Decimal64,
the Makefile.PL was configured to make the STRtoD64()
function available
for
your build of Math::Decimal64. Else
returns false.
C library doesn't provide the strtod64 function.)
#########################
$test
= is_ZeroD64(
$d64
);
Returns:
-1
if
$d64
is negative zero;
1
if
$d64
is zero, but not negative zero;
0
if
$d64
is not zero.
########################
$test
= is_InfD64(
$d64
);
Returns:
-1
if
$d64
is negative infinity;
1
if
$d64
is positive infinity;
0
if
$d64
is not infinity.
########################
$bool
= is_NaND64(
$d64
);
Returns:
1
if
$d64
is a NaN;
0
if
$d64
is not a NaN.
###################
LDtoD64(
$d64
,
$ld
);
# $ld is a Math::LongDouble object
D64toLD(
$ld
,
$d64
);
# $ld is a Math::LongDouble object
Conversions between Math::LongDouble and Math::Decimal64
objects - done by simply casting the long double value to a
_Decimal64 value, or (resp.) vice-versa.
Requires that Math::LongDouble
has
been loaded.
#######################
$sign
= get_sign(
$d64
);
Returns the sign (
'+'
or
'-'
) of
$d64
.
#####################
$exp
= get_exp(
$d64
);
Returns the exponent of
$d64
. This is the exponent value
that's stored internally within the encapsulated _Decimal64
value; it may differ from the value that you assigned.
For example,
if
you
've assigned the value MEtoD64('
100', 0)
it will probably be held internally as
'1e2'
, not
'100e0'
,
in which case get_exp() would
return
2, not 0.
####################
LICENSE
This program is free software; you may redistribute it and/or
modify it under the same terms as Perl itself.
Copyright 2012-17 Sisyphus
AUTHOR
Sisyphus <sisyphus at(@) cpan dot (.) org>