The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

# NAME

Data::Float::DoubleDouble - human-readable representation of the "double-double" long double

# AIM

``````  Mostly, one would use Data::Float to do what this module does.
But that module doesn't work with the 'double-double' type of
long double ... hence, this module.

Given a double-double value, we aim to be able to:
1) Convert that NV to its internal packed hex form;
2) Convert the packed hex form of 1) back to the original value;
3) Convert that NV to a more human-readable packed hex form,
similar to what Data::Float's float_hex function achieves;
4) Convert the packed hex form of 3) back to the original value;

For 1) we use NV2H().
For 2) we use H2NV().
For 3) we use float_H().
For 4) we use H_float().

We also have float_B and B_float which are the base 2
equivalents of float_H and H_float.``````

# FUNCTIONS

``````  #############################################

\$hex = NV2H(\$nv);

Unpacks the NV to a string of 32 hex characters.
The first 16 characters relate to the value of the most significant
double:
Characters 1 to 3 (incl) embody the sign of the mantissa, the value
of the exponent, and the value (0 or 1) of the implied leading bit.
Characters 4 to 16 (incl) embody the value of the 52-bit mantissa.

The second 16 characters (17 to 32) relate to the value of the least
siginificant double:
Characters 17 to 19 (incl) embody the sign of the mantissa, the
value of the exponent, and the value (0 or 1) of the implied
Characters 20 to 32 (incl) embody the value of the 52-bit mantissa.

For a more human-readable hex representation, use float_H().

#############################################

\$nv = H2NV(\$hex);

For \$hex written in the format returned by NV2H, H2NV(\$hex)
returns the NV.

#############################################

\$hex = D2H(\$nv);

Treats the NV as a double and returns a string of 16 hex characters.
Characters 1 to 3 (incl) embody the sign of the mantissa, the value
(0 or 1) of the implied leading bit and the value of the exponent.
Characters 4 to 16 (incl) embody the value of the 52-bit mantissa
of the first double.

#############################################

\$nv = H2D(\$hex, \$opt); # Second arg is optional

For \$hex written in the format returned by D2H, H2D(\$hex) returns
the NV.

#############################################

\$readable_hex = float_H(\$nv, \$opt); # Aliased to float_hex
# \$opt is optional

For *most* NVs, returns a 106-bit hex representation of the NV
(long double) \$nv in the format
s0xd.hhhhhhhhhhhhhhhhhhhhhhhhhhhpe where:
s is the sign (either '-' or '+')
0x is literally "0x"
d is the leading (first) bit of the number (either '1' or '0')
. is literally "." (the decimal point)
hhhhhhhhhhhhhhhhhhhhhhhhhhh is a string of 27 hex digits
representing the remaining 105 bits
of the mantissa.
p is a literal "p" that separates mantissa from exponent
e is the (signed) exponent

The keen mind will have realised that 27 hex digits encode 108
(not 105) bits. However, the last 3 bits are to be ignored and
will always be zero for a 106-bit float. Thus the 27th hex
character for a 106-bit float will either be "8" (representing
a "1") or "0" (representing a "0") for the 106th bit.

BUT: Some NV values encapsulate a value that require more than
106 bits in order to be correctly represented.
If the string that float_H returns is larger than as
described above, then it will, however,  have returned a
string that contains the *minimum* number of characters
needed to accurately represent the given value.
As an extreme example: the double-double arrangement can
represent the value 2**1023 + 2**-1074, but to express
that value as a stream of bits requires 2098 bits, and to
express that value in the format that float_H returns
requires 526 hex characters (all of which are zero, except
for the first and the last). When you add the sign, radix
point, exponent, etc., the float_H representation of that
value consists of 535 characters.

If a second arg is provided, it must be the string 'raw' - in
which case infs/nans will be returned in hex format instead of
as "inf"/"nan" strings.

#############################################

As for float_H, but uses C's sprintf() function to do the
conversion to the hex string. The second arg (\$fmt) can be either
"%La" (in which case the alphabetic characters will be lower
case) or "%LA" (in which case the alphabetic characters will be
upper case).
Unlike float_H, this function cannot take the 'raw' argument.
And, unlike float_H, this function will not return values that
require more than 106 bits to be expressed.

#############################################

As for float_H, but standardises the format to be the same as I
get for DD2HEX. That is, there's no leading + for positive
values, positive and zero exponents are prefixed with a +,
trailing zeroes in the mantissa are removed, and zeroes are
presented as (-)0x0p+0 or (-)0X0P+0. As for DD2HEX, the second
arg (\$fmt) can be either "%La" or "%LA" (nothing else) and that
determines whether the alphabetic characters are lower case or
upper case.
Unlike float_H, this function cannot take the 'raw' argument.
Like float_H it will, however, accurately express the value
that's encapsulated in the double-double (even though that
minimum may exceed the usual 27 hex digits).

#############################################

\$readable = express(\$nv, \$opt); # \$opt is an optional arg.

An alternative way of assessing the value of the double-double.
Express the double as msd + lsd, where the 2 doubles (msd and lsd)
are written in scientic notation. The doubles will be written in
decimal format unless a second arg of 'h' or 'H' is provided - in
which case they will be written in hex (respectively capitalised
hex) format.
The second arg (\$opt), if provided, must be either 'h' or 'H'.

#############################################

\$nv = H_float(\$hex);

For \$hex written in the format returned by float_H(), returns
the NV that corresponds to \$hex.

#############################################

@bin = float_B(\$nv, \$opt); # Second arg isoptional

Returns the sign, the mantissa (as a base 2 string), and the
exponent of \$nv. (There's an implied radix point between the
first and second digits of the mantissa).
For nan/inf, the mantissa is 'nan' or 'inf' respectively unless
2nd arg is literally 'raw' - in which case it will be a base 2
version of the nan/inf encoding.

#############################################

@bin = float_H2B(\$hex, \$opt); # Second arg is optional

As for the above float_B() function - but takes the hex
string of the NV (as returned by float_H) as its argument,
For a more direct way of obtaining the array, use float_B
If a second arg is provided, it must be the string 'raw' - in
which case inf/nan mantissas will be returned in hex format

#############################################

@bin = NV2binary(\$nv);

Another way of arriving at (almost) the same binary representation
of the NV -ie as an array consisting of (sign, mantissa, exponent).
The mantissa if Infs and NaNs will be returned as 'inf' or 'nan'
respectively and the sign associated with the nan will always
be '+'.
With this function, trailing zeroes are stripped from the mantissa
and exponents for 0, inf and nan might not match the other binary
representations.
This function is based on code from the mpfr library's
tests/tset_ld.c file.

#############################################

\$hex = B2float_H(@bin, \$opt); # \$opt is an optional arg

The reverse of float_H2B. It takes the array returned by
either float_B or float_H2B as its arguments, and returns
the corresponding hex form.
If \$opt is provided and is the string 'raw', the actual
hex encoding of any nan/inf will be returned - instead of
the string "inf" or "nan" respectively.

#############################################

(\$sign1, \$sign2) = get_sign(\$nv);

Returns the signs of the two doubles contained in \$nv.

#############################################

(\$exp1, \$exp2) = get_exp(\$nv);

Returns the exponents of the two doubles contained in \$nv.

#############################################

(\$double1, \$double2) = get_doubles(\$nv);

Returns the two doubles contained in \$nv.

#############################################

(\$mantissa1, \$mantissa2) = get_mant_H(NV2H(\$nv));

Returns an array of the two 52-bit mantissa components of
the two doubles in their hex form. The values of the
implied leading (most significant) bits are not provided,
nor are the values of the two exponents.

#############################################

\$intermediate_zeroes = inter_zero(get_exp(\$nv));

Returns the number of zeroes that need to come between the
mantissas of the 2 doubles when \$nv is translated to the
representation that float_H() returns.

#############################################

\$bool = are_inf(@nv); # Aliased to float_is_infinite.

Returns true if and only if all of the (NV) arguments are
infinities.
Else returns false.

#############################################

\$bool = are_nan(@nv); # Aliased to float_is_nan.

Returns true if and only if all of the (NV) arguments are
NaNs. Else returns false.

#############################################

\$hex = dd_bytes(\$nv);

Returns same as NV2H(\$nv).

#############################################

For Compatibility with Data::Float:

#############################################

\$class = float_class(\$nv);

Returns one of either "NAN", "INFINITE", "ZERO", "NORMAL"
or "SUBNORMAL" - whichever is appropriate. (The NV must
belong to one (and only one) class.

#############################################

\$bool = float_is_nan(\$nv); # Alias for are_nan()

Returns true if \$nv is a NaN.
Else returns false.

#############################################

\$bool = float_is_infinite(\$nv); # Alias for are_inf()

Returns true if \$nv is infinite.
Else returns false.

#############################################

\$bool = float_is_finite(\$nv);

Returns true if NV is neither infinite nor a NaN.
Else returns false.

#############################################

\$bool = float_is_nzfinite(\$nv);

Returns true if NV is neither infinite, nor a NaN, nor zero.
Else returns false.

#############################################

\$bool = float_is_zero(\$nv);

Returns true if NV is zero.
Else returns false.

#############################################

\$bool = float_is_normal(\$nv);

Returns true if NV is finite && non-zero && the implied
leading digit in its internal representation is '1'.
Else returns false.

#############################################

\$bool = float_is_subnormal(\$nv);

Returns true if NV is finite && non-zero && the implied
leading digit in its internal representation is '0'.

#############################################

\$nv = nextafter(\$nv1, \$nv2);

\$nv1 and \$nv2 must both be floating point values. Returns the
next representable floating point value adjacent to \$nv1 in the
direction of \$nv2, or returns \$nv2 if it is numerically
equal to \$nv1. Infinite values are regarded as being adjacent to
the largest representable finite values. Zero counts as one value,
even if it is signed, and it is adjacent to the positive and
negative smallest representable finite values. If a zero is returned
then it has the same sign as \$nv1. Returns
NaN if either argument is a NaN.

#############################################

\$nv = nextup(\$nv1);

\$nv1 must be a floating point value. Returns the next representable
floating point value adjacent to \$nv1 with a numerical value that
is strictly greater than \$nv1, or returns \$nv1 unchanged if there
is no such value. Infinite values are regarded as being adjacent to
the largest representable finite values. Zero counts as one value,
even if it is signed, and it is adjacent to the smallest
representable positive and negative finite values. If a zero is
returned, because \$nv1 is the smallest representable negative
value, and zeroes are signed, it is a negative zero that is
returned. Returns NaN if \$nv1 is a NaN.

#############################################

\$nv = nextdown(\$nv1);

\$nv1 must be a floating point value. Returns the next representable
floating point value adjacent to \$nv1 with a numerical value that
is strictly less than \$nv1, or returns \$nv1 unchanged if there is
no such value. Infinite values are regarded as being adjacent to the
largest representable finite values. Zero counts as one value, even
if it is signed, and it is adjacent to the smallest representable
positive and negative finite values. If a zero is returned, because
\$nv is the smallest representable positive value, and zeroes are
signed, it is a positive zero that is returned. Returns NaN if VALUE
is a NaN.

#############################################
#############################################``````

# TODO

``````   Over time, introduce the features of (and functions provided by)
Data::Float``````

``````   This program is free software; you may redistribute it and/or
``   Sisyphus <sisyphus at(@) cpan dot (.) org>``