NAME
Math::Ryu - perl interface to Ryu (sane NV-to-string conversion).
DEPENDENCIES
This module uses the Ryu C implementation, which is included
with
this module's source (in the Ryu_Library/ryu folder). It is therefore
unnecessary to download that Ryu code - but, should you wish to grab
it, it's also available at:
PREAMBLE
NVs of type
'double'
,
'__float128'
and
'long double'
(but not
DoubleDouble), are now supported, though the
'long double'
and
'__float128'
support requires that the underlying C compiler
provides the __uint128_t data type. This caveat probably rules out
'long double'
and
'__float128'
support on old 32-bit builds of perl,
but not on the vast majority of modern 64-bit builds.
DESCRIPTION
Using functionality provided by the Ryu library, we convert an NV to
a decimal string, such that
no
information is lost, yet keeping the
string as short as possible.
Stringification of NVs provided by perl's
() fuction does not
always adhere to this principle - that is, the condition
(
"$nv"
==
$nv
) is not always true.
For example:
$nv
= 1.4 / 10;
# "$nv" != $nv
We can address this by using (s)
printf
. For example, on a perl whose
$Config
{nvtype} is
'double'
, we could
do
sprintf
(
"%.17g"
,
$nv
)
to provide the stringification. Then we will find that, except
for
NaN, the condition (
sprintf
(
"%.17g"
,
$nv
) ==
$nv
) is always true.
But this presents another problem in that
"%.17g"
formatting will
sometimes provide more significant decimal digits than are needed.
For example:
printf
(
"%.17g"
, 0.1);
# prints '0.10000000000000001', even though
# the condition ("0.1" == 0.1) is also true.
This module provides the nv2s() function which returns a decimal
string representation of its (NV) argumennt. Unless
$nv
is NaN:
1) the condition ( nv2s(
$nv
) ==
$nv
) is always true;
and
2) the string returned by nv2s(
$nv
) will contain the fewest number of
siginificant decimal digits needed
for
that first condition to hold.
The aim is that the formatting of the string returned by nv2s() will
be the same as provided by python3's
() function.
(Please report any variances - especially substantive ones.)
SYNOPSIS
1.0 of Math::Ryu. From version 1.0 on, nv2s() is the usual
"goto"
function. Some (not-very-useful) functions from the earlier versions
have been removed as of version 1.0. See the
"FUNCTIONS"
documentation
below
for
a full list of current public functions.
my
$str
= nv2s(1 / 10);
$str
;
# 0.1 (double|long double|__float28)
$str
= nv2s(1.4 / 10);
$str
;
# 0.13999999999999999 (double)
# 0.14 (80-bit long double)
# 0.13999999999999999999999999999999999 (__float128)
$str
= nv2s(
sqrt
2);
$str
;
# 1.4142135623730951 (double)
# 1.4142135623730950488 (80-bit long double)
# 1.4142135623730950488016887242096982 (__float128)
FUNCTIONS
$str
= nv2s(
$nv
);
Treats the argument as an NV, even
if
the arg's NOK flag is unset.
You can first check that treating
$nv
as an NV is appropriate by
verifying that is_NV(
$nv
), documented below, returns a TRUE value.
$str
is a string in floating point
format
that accurately and
succinctly represents the value held by
$nv
. When
$str
is correctly
assigned to a perl number, that number will be equivalent to
$nv
.
Also,
$str
will contain as few digits as possible, without
compromising that accuracy.
$str
is formatted by fmtpy() - documented below. (The
format
is the
same as provided by Python3's
() function
for
the same value.)
There is
no
need
for
the
caller
to know the type (nvtype) of NV that
$nv
is - the nv2s() function handles that.
Note: nv2s() spends some
time
reformatting the string that the ryu
library returned. If you are happy enough
with
that original
string, then save some
time
by avoiding the reformating and
just run d2s() or ld2s() or q2s() - whichever is appropriate
for
your nvtype. (Or employ your own reformatting
sub
.)
Run the script in the
"benchmark"
folder of Math-Ryu source
distro (perl benchmark.p),
for
comparative timings of
"with"
and
"without"
reformatting.
$str
= n2s(
$sv
);
Use this function
if
the argument is to be evaluated in numeric context.
Arguments that are perl references will throw an exception.
Handles
"integer"
,
"floating point"
and
"string"
values
. If the
argument is a floating point numeric value, then this function returns
the same as nv2s(). Otherwise, it stringifies the numeric value of the
given
argument, according to whether that numeric value is an integer
value (that fits into an IV or UV) or a floating point value.
If the argument (
$sv
) is itself a string, then
$str
will reflect the
value of
$sv
considered in numeric context. For example
n2s(
'hello world'
) would
return
'0.0'
, not
'hello world'
.
$str
= d2s(
$nv
);
# Use only when $Config{nvtype} is 'double'.
Returns a decimal string representation of the value of
$nv
in the
format
'mantissaEexponent'
- eg
'1E-1'
(
for
0.1) and
'1.4142135623730951E0'
(
for
square root of 2).
$str
= ld2s(
$nv
);
# Use only when $Config{nvtype} is 80-bit extended
# precision 'long double'.
Returns a decimal string representation of the value of
$nv
in the
format
'mantissaEexponent'
- eg
'1E-1'
(
for
0.1) and
'1.4142135623730950488E0'
(
for
square root of 2).
$str
= q2s(
$nv
);
# Use only when $Config{nvtype} is either
# '__float128' or IEEE 754 'long double'.
Returns a decimal string representation of the value of
$nv
in the
format
'mantissaEexponent'
- eg
'1E-1'
(
for
0.1) and
'1.4142135623730950488016887242096982E0'
(
for
square root of 2).
$bool
= is_NV(
$arg
);
Returns 1
if
$arg
should be deemed an NV (perl
scalar
floating
point value)
for
the purposes of this module.
Else returns 0.
(If is_NV(
$arg
) returns 0, then calling nv2s(
$arg
) might not
return
a useful representation.)
$str
= fmtpy(
$s
);
( Originally a pure perl
sub
- still accessible as fmtpy_pp(). )
The argument
$s
is a string as returned by either d2s() or ld2s()
or q2s(). We aim to reformat
$s
to be the same as the string that
python3 would produce, and then
return
it as
$str
.
NOTE: Doing
'fmtpy(d2s($nv))'
or
'fmtpy(ld2s($nv))'
or
'fmtpy(q2s($nv))'
is equivalent to doing
'nv2s($nv))'
.
$bool
= ryu_lln(
$sv
);
This is an interface to the API function
'looks_like_number'
.
Returns 1
if
$sv
looks like a number,
else
returns 0.
There's not really any reason to export this subroutine, but
it
's there to use if it'
s ever helpful.
It's NOT identical to Scalar::Util::looks_like_number().
$bool
= ryu_SvIOK(
$sv
);
$bool
= ryu_SvNOK(
$sv
);
$bool
= ryu_SvPOK(
$sv
);
$bool
= ryu_SvIOKp(
$sv
);
Return 1
if
(respectively) the IOK or NOK or POK or IOKp) flag of
$sv
is set.
Else
return
0.
$nv
= s2d(
$str
);
# string to double
Available only to perls whose
$Config
{nvsize} is 8.
If ryu_lln(
$str
) returns a false value, then the s2d() call
will
die
with
an appropriate error message.
Otherwise, it returns an NV (double) of the specified value.
(This can be useful on pre-5.30.0 perls, where perl frequently
assigns
values
incorrectly.)
PRINT FUNCTIONS
Just some shorthand ways of printing out
values
to STDOUT.
pnv(
$nv
);
# mnemonic: "print nv2s($nv)"
snv(
$nv
);
# mnemonic: "say nv2s($nv)"
(nv2s is documented above in
"FUNCTIONS"
.)
prints nv2s(
$nv
) to STDOUT
and respectively:
prints nv2s(
$nv
).
"\n"
to STDOUT.
pn(
$sv
);
# mnemonic: "print n2s($sv)"
sn(
$sv
);
# mnemonic: "say n2s($sv)"
(n2s is documented above in
"FUNCTIONS"
.)
prints n2s(
$sv
) to STDOUT
and respectively:
prints n2s(
$sv
).
"\n"
to STDOUT.
pany(
@args
);
# "p"rint "any"
sany(
@args
);
# "s"ay "any"
Both functions simply
do
a
"print $arg;"
if
the perl API function
looks_like_number(
$arg
) returns true && either:
a) the POK flag is set;
or
b) the IOK flag is set.
If looks_like_number(
$arg
) returns true but both the POK and
IOK flags are unset, then
$arg
must be an NV.
If that NV fits into an IV, then cast it to an IV, and
out
that (IV) value. Else
out nv2s(
$arg
).
For all other arguments (ie those
for
which looks_like_number(
$arg
)
has
returned false), simply
"print $arg;"
.
After sany()
has
printed the final arg, a terminating
"\n"
is printed.
Otherwise sany() and pany() are identical.
We should find that pany() and sany() produce outputs aidentical to
() - except
for
those NV
values
that perl would misrepresent.
The aim is that pany/sany alone will suffice, and there is
no
need
for
the user to ever call perl's
()/
say
() functions
if
Math::Ryu
has
been loaded.
Of course,
if
you really
do
want to
out an NV as stringified
by perl (as opposed to being stringified by nv2s) then you'll need
to
use
/
say
, not pany()/sany().
Unlike
"print"
and
"say"
, which are generally called without
bracketed args,
"pany()"
and
"sany()"
should be called
with
their
argument(s) enclosed in brackets
if
any of the arguments are
object constructors - eg Math::BigInt->new(42).
Otherwise it's probably safe to dispense
with
the brackets - though,
for
safety, I recommend using the brackets, anyway.
To demonstrate this failing,
try
the following one-liner:
> perl -MMath::Ryu=
":all"
-MMath::BigInt -e
"pany Math::BigInt->new(1);"
Can't locate object method
"pany"
via
package
"Math::BigInt"
at -e line 1.
SPRINTF FUNCTION
$str
= spanyf(
@args
);
# No "format" arg - not *really* a "sprintf"
Return the string that pany(
@args
) would
.
Note that the arguments
given
to spanyf() should,
for
safety be
enclosed in brackets, just as
with
pany() and sany() - though again
this may well be unnecessary
if
the argument(s) don't include
an object constructor.
DISPLAYING INFS AND NANS
Originally, infinities and nans were displayed by nv2s() as
"inf"
,
"-inf"
, or
"nan"
. And this is still the guaranteed case
if
$Math::Ryu::PERL_INFNAN
is set to 0 (which is its initial value).
If the value of
$Math::Ryu::PERL_INFNAN
is set to a TRUE value, then
infinities and nans will be stringified according to perl's rules.
LICENSE
This perl module is free software; you may redistribute it
and/or modify it under the same terms as Perl itself.
However, the
".c"
and
".h"
files that ship
with
this distro
are Copyright 2018 Ulf Adams, and can only be used under the
the terms of version 2.0 the Apache License, or of version 1.0
of the Boost Software License.
A copy of the Apache License (Apache_License.txt) is included
with
this distro.
The Apache License can also be found at:
The Boost Software License can be found at:
This perl module is Copyright 2024 Sisyphus
=head1 AUTHOR
Sisyphus <sisyphus at(@) cpan dot (.) org>