Data-Float

 view release on metacpan or  search on metacpan

lib/Data/Float.pm  view on Meta::CPAN

my $have_subnormal;
{
	my $testval = $min_finite * 1.5;
	$have_subnormal = $testval == $min_finite ||
				$testval == ($min_finite + $min_finite);
}

_mk_constant("have_subnormal", $have_subnormal);

my $min_normal_exp = $have_subnormal ?
			$min_finite_exp + $significand_bits :
			$min_finite_exp;
my $min_normal = $have_subnormal ?
			mult_pow2($min_finite, $significand_bits) :
			$min_finite;

_mk_constant("min_normal_exp", $min_normal_exp);
_mk_constant("min_normal", $min_normal);

#
# Feature tests.
#

my $have_signed_zero = sprintf("%e", -0.0) =~ /\A-/;
_mk_constant("have_signed_zero", $have_signed_zero);
my($pos_zero, $neg_zero);
if($have_signed_zero) {
	$pos_zero = +0.0;
	$neg_zero = -0.0;
	my $tzero = -0.0;
	{ no warnings "void"; $tzero == $tzero; }
	my $ntzero = -$tzero;
	if(sprintf("%e", -$ntzero) =~ /\A-/) {
		_mk_constant("pos_zero", $pos_zero);
		_mk_constant("neg_zero", $neg_zero);
	} else {
		# Zeroes lose their signedness upon arithmetic operations.
		# Therefore make the pos_zero and neg_zero functions
		# return fresh zeroes to avoid trouble.
		*pos_zero = sub () { my $ret = $pos_zero };
		*neg_zero = sub () { my $ret = $neg_zero };
		push @EXPORT_OK, "pos_zero", "neg_zero";
	}
}

my($have_infinite, $pos_infinity, $neg_infinity);
{
	my $testval = $max_finite * $max_finite;
	$have_infinite = $testval == $testval && $testval != $max_finite;
	_mk_constant("have_infinite", $have_infinite);
	if($have_infinite) {
		_mk_constant("pos_infinity", $pos_infinity = $testval);
		_mk_constant("neg_infinity", $neg_infinity = -$testval);
	}
}

my $max_number = $have_infinite ? $pos_infinity : $max_finite;
_mk_constant("max_number", $max_number);

my($have_nan, $nan);
foreach my $nan_formula (
		'$have_infinite && $pos_infinity/$pos_infinity',
		'log(-1.0)',
		'0.0/0.0',
		'"nan"') {
	my $maybe_nan =
		eval 'local $SIG{__DIE__}; local $SIG{__WARN__} = sub { }; '.
		     $nan_formula;
	if(do { local $SIG{__WARN__} = sub { }; $maybe_nan != $maybe_nan }) {
		$have_nan = 1;
		$nan = $maybe_nan;
		_mk_constant("nan", $nan);
		last;
	}
}
_mk_constant("have_nan", $have_nan);

# The rest of the code is parsed after the constants have been calculated
# and installed, so that it can benefit from their constancy.
{
	local $/ = undef;
	my $code = <DATA>;
	close(DATA);
	{
		local $SIG{__DIE__};
		eval $code;
	}
	die $@ if $@ ne "";
}

1;

__DATA__

=head1 FUNCTIONS

Each "float_" function takes a floating point argument to operate on.  The
argument must be a native floating point value, or a native integer with
a value that can be represented in floating point.  Giving a non-numeric
argument will cause mayhem.  See L<Params::Classify/is_number> for a way
to check for numericness.  Only the numeric value of the scalar is used;
the string value is completely ignored, so dualvars are not a problem.

=head2 Classification

Each "float_is_" function returns a simple truth value result.

=over

=item float_class(VALUE)

Determines which of the five classes described above VALUE falls
into. Returns "NORMAL", "SUBNORMAL", "ZERO", "INFINITE", or "NAN"
accordingly.

=cut

sub float_class($) {
	my($val) = @_;
	return "ZERO" if $val == 0.0;
	return "NAN" if $val != $val;
	$val = -$val if $val < 0;
	return "INFINITE" if have_infinite && $val == $pos_infinity;
	return have_subnormal && $val < min_normal ? "SUBNORMAL" : "NORMAL";
}

=item float_is_normal(VALUE)



( run in 0.500 second using v1.01-cache-2.11-cpan-e1769b4cff6 )