=pod

=head1 NAME

Math::Ryu - perl interface to Ryu (sane NV-to-string conversion).

=head1 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:
   L<https://github.com/ulfjack/ryu/tree/master/ryu>

=head1 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.

=head1 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 print() 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) argument.
   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
       significant 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 print() function.
   (Please report any variances - especially substantive ones.)

=head1 SYNOPSIS

   For this synopsis, we use nv2s() which first appears in version
   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.

   use Math::Ryu qw(:all);

   my $str = nv2s(1 / 10);
   print $str;           # 0.1 (double|long double|__float28)

   $str = nv2s(1.4 / 10);
   print $str; # 0.13999999999999999 (double)
               # 0.14 (80-bit long double)
               # 0.13999999999999999999999999999999999 (__float128)

   $str = nv2s(sqrt 2);
   print $str; # 1.4142135623730951 (double)
               # 1.4142135623730950488 (80-bit long double)
               # 1.4142135623730950488016887242096982 (__float128)

=head1 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 print() function for the same value.)
    There is no need for the caller to know the type ($Config{nvtype})
    of $nv - 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 reformatting and
          instead run d2s() or ld2s() or q2s() - whichever is appropriate.
          See the d2s/ld2s/q2s documentation (below) to determine which
          is appropriate for your perl.
          Run the script in the "benchmark" folder of Math-Ryu source
          distro (perl benchmark.p), for comparative timings of "with"
          and "without" reformatting.
          You are, of course, free to employ your own reformatting sub.

   $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') will return '0.0', not 'hello world';
     n2s('0.10000000000000001') will return '0.1' if nvtype is 'double'.

 $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).

  $str = fx2s($nv);
    Cast $nv to a single precision float value.
    Returns a decimal string representation of that single precision
    float value in the format 'mantissaEexponent' - eg:
      '1E-1' (for 0.1) and
      '1.4142135E0' (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: If it's appropriate to do'fmtpy(d2s($nv))' then that's
          the same as doing 'nv2s($nv))'.
          If it's appropriate to do'fmtpy(ld2s($nv))' then that's
          the same as doing 'nv2s($nv))'.
          If it's appropriate to do'fmtpy(q2s($nv))' then that's
          the same as 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 does NOT handle MAGIC and is therefore 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.)


=head1 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"
   For each of the arguments ($_) in @args, both functions simply do a
   "print $_;" if the perl API function looks_like_number($_) returns
   true && either:
     a) the POK flag for $_ is set;
     or
     b) the IOK flag for $_ is set.

   If looks_like_number($_) 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 print out
   that (IV) value. Else print out nv2s($arg).

   NOTE: If perl's NV precision is less than IV precision (as is the most
         common perl configuration) then pany() can produce a result
         that differs from nv2s(). Eg: pany(6.886264645392433e+16) will,
         in accordance with this documentation, output 68862646453924328.
         But nv2s(6.886264645392433e+16) will, in accordance with its
         documentation return the float string 6.886264645392433e+16.
         Aside from that, "pany($nv);" and "print nv2s($nv);" will
         produce identical outputs - assuming $nv is an NV, of course.

   For all other arguments (ie those for which looks_like_number($arg)
   has returned false), pany() will simply "print $_;".

   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 that are
   identical to print()/say() - except for those NV values for which
   perl's interpolation differs from the string returned by nv2s().
   The aim is that pany/sany alone will suffice, and there is no need
   for the user to ever call perl's print()/say() functions if
   Math::Ryu has been loaded.
   Of course, if you really do want to print out an NV as stringified
   by perl then you'll need to use print/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.

=head1 SPRINTF FUNCTION

  $str = spanyf(@args); # No "format" arg - not *really* a "sprintf"
   Return the string that pany(@args) would print.
   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.

=head1 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.

=head1 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:
   http://www.apache.org/licenses/LICENSE-2.0

   The Boost Software License can be found at:
   https://www.boost.org/LICENSE_1_0.txt

   This perl module is Copyright 2024-25 Sisyphus
=head1 AUTHOR

   Sisyphus <sisyphus at(@) cpan dot (.) org>

=cut
