## This file generated by InlineX::C2XS (version 0.22) using Inline::C (version 0.5002)
package Math::LongDouble;
use warnings;
use strict;
use POSIX;
require Exporter;
*import = \&Exporter::import;
require DynaLoader;
use overload
'+' => \&_overload_add,
'*' => \&_overload_mul,
'-' => \&_overload_sub,
'/' => \&_overload_div,
'**' => \&_overload_pow,
'+=' => \&_overload_add_eq,
'*=' => \&_overload_mul_eq,
'-=' => \&_overload_sub_eq,
'/=' => \&_overload_div_eq,
'**=' => \&_overload_pow_eq,
'==' => \&_overload_equiv,
'""' => \&_overload_string,
'!=' => \&_overload_not_equiv,
'bool' => \&_overload_true,
'!' => \&_overload_not,
'=' => \&_overload_copy,
'<' => \&_overload_lt,
'<=' => \&_overload_lte,
'>' => \&_overload_gt,
'>=' => \&_overload_gte,
'<=>' => \&_overload_spaceship,
'abs' => \&_overload_abs,
'int' => \&_overload_int,
'sqrt' => \&_overload_sqrt,
'log' => \&_overload_log,
'exp' => \&_overload_exp,
'sin' => \&_overload_sin,
'cos' => \&_overload_cos,
'atan2' => \&_overload_atan2,
'++' => \&_overload_inc,
'--' => \&_overload_dec,
;
use subs qw(LD_DBL_DIG LD_LDBL_DIG);
our $VERSION = '0.05';
#$VERSION = eval $VERSION;
DynaLoader::bootstrap Math::LongDouble $Math::LongDouble::VERSION;
@Math::LongDouble::EXPORT = ();
@Math::LongDouble::EXPORT_OK = qw(
InfLD NaNLD ZeroLD UnityLD is_NaNLD is_InfLD is_ZeroLD STRtoLD LDtoSTR NVtoLD UVtoLD IVtoLD
LDtoNV LDtoLD cmp_NV
ld_set_prec ld_get_prec LDtoSTRP
LD_DBL_DIG LD_LDBL_DIG
ld_max_orig_len ld_min_inter_prec ld_min_inter_base ld_max_orig_base
);
%Math::LongDouble::EXPORT_TAGS = (all => [qw(
InfLD NaNLD ZeroLD UnityLD is_NaNLD is_InfLD is_ZeroLD STRtoLD LDtoSTR NVtoLD UVtoLD IVtoLD
LDtoNV LDtoLD cmp_NV
ld_set_prec ld_get_prec LDtoSTRP
LD_DBL_DIG LD_LDBL_DIG
ld_max_orig_len ld_min_inter_prec ld_min_inter_base ld_max_orig_base
)]);
sub dl_load_flags {0} # Prevent DynaLoader from complaining and croaking
sub _overload_string {
if(is_ZeroLD($_[0])) {
return '-0' if is_ZeroLD($_[0]) < 0;
return '0';
}
if(is_NaNLD($_[0])) {return 'NaN'}
my $inf = is_InfLD($_[0]);
return '-Inf' if $inf < 0;
return 'Inf' if $inf > 0;
my @p = split /e/i, LDtoSTR($_[0]);
while(substr($p[0], -1, 1) eq '0' && substr($p[0], -2, 1) ne '.') {
chop $p[0];
}
return $p[0] . 'e' . $p[1];
}
sub new {
# This function caters for 2 possibilities:
# 1) that 'new' has been called OOP style - in which
# case there will be a maximum of 2 args
# 2) that 'new' has been called as a function - in
# which case there will be a maximum of 1 arg.
# If there are no args, then we just want to return a
# Math::LongDouble object that's a NaN.
if(!@_) {return NaNLD(1)}
if(@_ > 2) {die "More than 2 arguments supplied to new()"}
# If 'new' has been called OOP style, the first arg is the string
# "Math::LongDouble" which we don't need - so let's remove it. However,
# if the first arg is a Math::LongDouble object (which is a possibility),
# then we'll get a fatal error when we check it for equivalence to
# the string "Math::LongDouble". So we first need to check that it's
# not an object - which we'll do by using the ref() function:
if(!ref($_[0]) && $_[0] eq "Math::LongDouble") {
shift;
if(!@_) {return NaNLD(1)}
}
if(@_ > 1) {die "Too many arguments supplied to new() - expected no more than 1"}
my $arg = shift;
my $type = _itsa($arg);
if($type == 3) { # NV
if($arg == 0) {return STRtoLD($arg)}
if($arg != $arg) { #NaN
if($arg =~ /^\-/) {return NaNLD(-1)}
return NaNLD(1);
}
if(($arg / $arg) != ($arg / $arg)) { # Inf
if($arg < 0) {return InfLD(-1)}
return InfLD(1);
}
return STRtoLD($arg);
}
if(
$type == 1 || #UV
$type == 2 || #IV
$type == 4 #PV
) {
return STRtoLD($arg);
}
if($type == 96) { # Math::LongDouble object
return LDtoLD($arg);
}
die "Bad argument given to new";
}
sub LD_DBL_DIG {return _DBL_DIG()}
sub LD_LDBL_DIG {return _LDBL_DIG()}
sub ld_min_inter_prec {
die "Wrong number of args to minimum_intermediate_prec()" if @_ != 3;
my $orig_base = shift;
my $orig_length = shift;
my $to_base = shift;
return ceil(1 + ($orig_length * log($orig_base) / log($to_base)));
}
sub ld_min_inter_base {
die "Wrong number of args to minimum_intermediate_base()" if @_ != 3;
my $orig_base = shift;
my $orig_length = shift;
my $to_prec = shift;
return ceil(exp($orig_length * log($orig_base) / ($to_prec - 1)));
}
sub ld_max_orig_len {
die "Wrong number of args to maximum_orig_length()" if @_ != 3;
my $orig_base = shift;
my $to_base = shift;
my $to_prec = shift;
return floor(1 / (log($orig_base) / log($to_base) / ($to_prec - 1)));
}
sub ld_max_orig_base {
die "Wrong number of args to maximum_orig_base()" if @_ != 3;
my $orig_length = shift;
my $to_base = shift;
my $to_prec = shift;
return floor(exp(1 / ($orig_length / log($to_base) / ($to_prec -1))));
}
1;
__END__
=head1 NAME
Math::LongDouble - perl interface to C's long double operations
(for perls that don't already have that capability)
=head1 BUGS
This module has bugs on perls built with a Microsoft compiler (eg
ActivePerl) - even if the binaries installed onto the MSVC-built
perl were built using MinGW on a MinGW-built perl such as Strawberry
Perl (where no such problem exists).
By some means that is still unclear, the 'long double' precision
can apparently be reduced to 'double' precision whenever a
Math::LongDouble object is raised to a power (or a square root taken)
on MSVC-built perls.
This bug manifests itself in causing some test failures in t/cmp.t
and t/pow.t.
=head1 DESCRIPTION
If your perl's NV is a 'long double', then there's no point in using this
module. But if your perl's NV is a 'double', then this module provides
you with a way of performing arithmetic operations with long double
precision.
use Math::LongDouble qw(:all);
my $arg = 32.1;
my $ld1 = Math::LongDouble->new($arg);# Stringify $arg, then assign
# using C's strtold()
my $ld2 = NVtoLD($arg); # Assign the NV 32.1 to $ld2.
=head1 OVERLOADING
The following operations are overloaded:
+ - * / **
+= -= *= /= **=
!= == <= >= <=> < >
++ --
=
abs bool ! int print
sqrt log exp
sin cos atan2
Arguments to the overloaded operations must be Math::LongDouble
objects.
$ld = $ld + 3.1; # currently an error. Do instead:
$ld = $ld + Math::LongDouble->new('3.1');
=head1 ASSIGNMENT FUNCTIONS
The following create and assign a new Math::LongDouble.
$ld = Math::LongDouble->new($arg);
Returns a Math::LongDouble object to which the numeric value of $arg
has been assigned.
If no arg is supplied then $ld will be NaN.
$ld = UVtoLD($arg);
Returns a Math::LongDouble object to which the numeric (unsigned
integer) value of $arg has been assigned.
$ld = IVtoLD($arg);
Returns a Math::LongDouble object to which the numeric (signed
integer) value of $arg has been assigned.
$ld = NVtoLD($arg);
Returns a Math::LongDouble object to which the numeric (floating
point) value of $arg has been assigned.
$ld2 = LDtoLD($ld1);
Returns a Math::LongDouble object that is a copy of the
Math::LongDouble object provided as the argument.
Courtesy of overloading, this is in effect no different to doing:
$ld2 = $ld1;
$ld = STRtoLD($str);
Returns a Math::LongDouble object that has the value of the string
$str.
=head1 ASSIGNMENT OF INF, NAN, UNITY and ZERO
$ld = InfLD($sign);
If $sign < 0, returns a Math::LongDouble object set to
negative infinity; else returns a Math::LongDouble object set
to positive infinity.
$ld = NaNLD($sign);
If $sign < 0, returns a Math::longDouble object set to
negative NaN; else returns a Math::LongDouble object set to
positive NaN. It may be problematical as to whether a NaN
with the correct sign has been returned ... but, either way,
it should return a NaN.
$ld = ZeroLD($sign);
If $sign < 0, returns a Math::LongDouble object set to
negative zero; else returns a Math::LongDouble object set to
zero.
$ld = UnityLD($sign);
If $sign < 0, returns a Math::LongDouble object set to
negative one; else returns a Math::LongDouble object set to
one.
ld_set_prec($precision);
Sets the precision of stringified values to $precision decimal
digits.
$precision = ld_get_prec();
Returns the precision (in decimal digits) that will be used
when stringifying values (by printing them, or calling
LDtoSTR).
=head1 RETRIEVAL FUNCTIONS
The following functions provide ways of seeing the value of
Math::LongDouble objects.
$nv = LDtoNV($ld);
This function returns the value of the Math::LongDouble object to
a perl scalar (NV). It may not translate the value accurately.
$string = LDtoSTR($ld);
Returns the value of the Math::LongDouble object as a string.
The returned string will contain the same as is displayed by
"print $ld", except that print() will strip the trailing zeroes
in the mantissa (significand) whereas LDtoSTR won't.
By default, provides 18 decimal digits of precision. This can be
altered by specifying the desired precision (in decimal digits)
in a call to ld_set_prec.
$string = LDtoSTRP($ld, $precision);
Same as LDtoSTR, but takes an additional arg that specifies the
precision (in decimal digits) of the stringified return value.
=head1 OTHER FUNCTIONS
$bool = is_NaNLD($ld);
Returns 1 if $ld is a Math::LongDouble NaN.
Else returns 0
$int = is_InfLD($ld)
If the Math::LongDouble object $ld is -inf, returns -1.
If it is +inf, returns 1.
Otherwise returns 0.
$int = is_ZeroLD($ld);
If the Math::LongDouble object $ld is -0, returns -1.
If it is zero, returns 1.
Otherwise returns 0.
$int = cmp_NV($ld, $nv);
$nv can be any perl number - ie NV, UV or IV.
If the Math::LongDouble object $ld < $nv returns -1.
If it is > $nv, returns 1.
Otherwise returns 0.
=head1 BASE CONVERSIONS
$DBL_DIG = LD_DBL_DIG; # The value specified by float.h's DBL_DIG.
# Will be set to 0 if float.h doesn't define
# DBL_DIG.
$LDBL_DIG = LD_LDBL_DIG; # The value specified by float.h's LDBL_DIG.
# Will be set to 0 if float.h doesn't define
# LDBL_DIG.
$min_prec = ld_min_inter_prec($orig_base, $orig_length, $to_base);
$max_len = ld_max_orig_len($orig_base, $to_base, $to_prec);
$min_base = ld_min_inter_base($orig_base, $orig_length, $to_prec);
$max_base = ld_max_orig_base($orig_length, $to_base, $to_prec);
The last 4 of the above functions establish the relationship between
$orig_base, $orig_length, $to_base and $to_prec.
Given any 3 of those 4, there's a function there to determine the
value of the 4th.
Let's say we have some base 10 floating point numbers comprising 16
significant digits, and we want to convert those numbers to a base 2
data type (say, 'long double').
If we then convert the value of that long double to a 16-digit base 10
float are we guaranteed of getting the original value back ?
It all depends upon the precision of the 'long double' type, and the
min_inter_prec() subroutine will tell you what the minimum
required precision is (in order to be sure of getting the original
value back). We have:
$min_prec = ld_min_inter_prec($orig_base, $orig_length, $to_base);
In our example case that becomes:
$min_prec = ld_min_inter_prec(10, 16, 2);
which will set $min_prec to 55.
That is, so long as the long double type has a precision of at least 55
bits, you can pass 16-digit, base 10, floating point values to it and
back again, and be assured of retrieving the original value.
(Naturally, this is assuming absence of buggy behaviour, and correct
rounding practice.)
Similarly, you might like to know the maximum significant number of
base 10 digits that can be specified, when assigning to (say) a
53-bit double. We have:
$max_len = ld_max_orig_len($orig_base, $to_base, $to_prec);
For this second example that becomes:
$max_len = ld_max_orig_len(10, 2, 53);
which will set $max_len to 15.
That is, so long as your base 10 float consists of no more than 15
siginificant digits, you can pass it to a 53-bit double and back again,
and be assured of retrieving the original value.
(Again, we assume absence of bugs and correct rounding practice.)
It is to be expected that
ld_max_orig_len(10, 2, $double_prec)
and
ld_max_orig_len(10, 2, $long_double_prec)
will (resp.) return the same values as LD_DBL_DIG and LD_LDBL_DIG.
($double_prec is the precision, in bits, of the C 'double' type,
and $long_double_prec is the precision, in bits, of the C 'long double'
type.)
The last 2 of the above subroutines (ie ld_min_inter_base and
ld_max_orig_base) are provided mainly for completeness.
Normally, there wouldn't be a need to use these last 2 forms ... but
who knows ...
The above examples demonstrate usage in relation to conversion between
bases 2 and 10. The functions apply just as well to conversions between
bases of any values.
The Math::MPFR module provides 4 identical functions, prefixed with
'mpfr_' instead of 'ld_' (to avoid name clashes).
Similarly, it provides constants (prefixed with 'MPFR_' instead of
'LD_') that reflect the values of float.h's DBL_DIG and LDBL_DIG.
=head1 LICENSE
This program is free software; you may redistribute it and/or modify
it under the same terms as Perl itself.
Copyright 2012, 2013 Sisyphus
=head1 AUTHOR
Sisyphus <sisyphus at(@) cpan dot (.) org>
=cut