How do I create or test for NaN or infinite values in Perl?
Here's a fairly reliable way:
my $inf = 9**9**9;
my $neginf = -9**9**9;
my $nan = -sin(9**9**9);
sub isinf { $_[0]==9**9**9 || $_[0]==-9**9**9 }
sub isnan { ! defined( $_[0] <=> 9**9**9 ) }
# useful for detecting negative zero
sub signbit { substr( sprintf( '%g', $_[0] ), 0, 1 ) eq '-' }
for my $num ( $inf, $neginf, $nan ) {
printf("%s:\tisinf: %d,\tisnan: %d,\tsignbit: %d\n", $num, isinf($num), isnan($num), signbit($num));
}
Output is:
inf: isinf: 1, isnan: 0, signbit: 0
-inf: isinf: 1, isnan: 0, signbit: 1
nan: isinf: 0, isnan: 1, signbit: 0
print "Is NaN\n" if $a eq 'nan';
print "Is Inf\n" if $a eq 'inf' or $a eq '-inf';
EDIT: Fixed for negative infinity.
perl -le 'sub isnan { ! defined( $_[0] <=> 9**9**9 ) }; print isnan("nan")'
if you don't trust me. –
Oenone Personally, I would use Math::BigFloat
(or BigInt
) for anything that is going to touch infinity of NaN
.
Why reinvent the wheel with a hack solution when there are already modules that do the job?
Use Data::Float from CPAN. It exports the following functions:
float_is_infinite(
)float_is_nan()
- ...
And contrary to the others half-working solutions posted here, it has a testsuite.
Data::Float
s check for NaN
is dependent on NaN
support on the platform. –
Paganism When I searched I got this site (here) and http://www.learning-perl.com/2015/05/perls-special-not-a-numbers/
The linked article points out that "nan" == "nan" is never true, when the underlying c implementation supports NaN because Nan cannot match itself.
This is nicely illustrated with
die "This perl does not support NaN!\n" if "NaN" == "NaN";
I guess the risk of running you code in an environment where perl has degraded gracefully and your code has not might be low enough so that you don't worry too much.
And of course if you don't want perl to interpolate as a number, use 'eq' not '=='
sub is_nan($) { $_[0] != $_[0] }
sub is_inf($) { scalar $_[0] =~ /^-?inf$/ }
NaN is NaN
on input (but not on output; there it's nan
(for whatever reason)).
And Perl understands inf
and -inf
:
DB<31> x is_nan 7
0 ''
DB<32> x is_nan NaN
0 1
DB<33> x is_nan 'NaN'
0 1
DB<34> x is_nan inf
0 ''
DB<38> x is_inf 4
0 ''
DB<39> x is_inf inf
0 1
DB<40> x is_inf -inf
0 1
DB<41> x is_inf '-inf'
0 1
DB<42> x is_nan 'NaN' + 3
0 1
DB<43> x is_nan 'NaN' + inf
0 1
DB<44> x is_nan inf
0 ''
DB<45> x is_nan inf - inf
0 1
DB<46> x is_nan -inf + inf
0 1
DB<47> x inf > 0
0 1
DB<48> x inf < 0
0 ''
DB<49> x -inf > 0
0 ''
DB<50> x -inf < 0
0 1
^
: I'm forced to use Windows, where some keys are "dead keys", so sometimes they are swallowed, even if I had typed them -- corrected, thanks for the hint! –
Ostracoderm Succinct answer that works follows.
1: How to create a "NAN" variable for output (to printf, for example):
{no strict 'subs'; $NAN="NAN"+1;}
2: How to test for "NAN" (looks like ascii art):
sub isnan {!($_[0]<=0||$_[0]>=0)}
3: How to create an "INF" and INFN variables:
{$INF="INF"+1; $INFN=-"INF"+1}
4: How to test for "INF" (of any sign):
sub isinf {($_[0]==+"INF")||($_[0]==-"INF")}
$NAN = "NAN"+1;
, $INF = "INF"+1; $INFN = -"INF";
just more verbose –
Photochromy no strict 'subs'
anymore if you don't use barewords. So your solution (still without testsuite) could be more succint. And isinf
is still not efficient. –
Tonguelashing © 2022 - 2024 — McMap. All rights reserved.