How do I create or test for NaN or infinity in Perl?
Asked Answered
P

7

26

How do I create or test for NaN or infinite values in Perl?

Photochromy answered 26/7, 2009 at 23:5 Comment(0)
P
15

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
Photochromy answered 26/7, 2009 at 23:7 Comment(9)
On 5.10 and above, where the C library supports it, just 0+"nan", 0+"inf", or 0+"-inf" work too.Photochromy
Very energetic of you: 13 minutes ago, you ask the question; 11 minutes ago, you answer it; 9 minutes ago, you post a comment. You should buy yourself a beer or something.Georas
Interesting, where does the 9 to the 9th to the 9th power come from? Is there a special significance or is it just an easy way to get a number big enough to blow up a variety of float specifications?Electroscope
@daotoad: yes, just an easy way. Some code unfortunately used things like 100**1000, which is infinite with IEEE double precision, but not infinite with long doubles.Photochromy
Just don't use this under bigint or you'll wonder why your program is hung.Ghana
Right, under bigint, use Math::BigInt->bnan(), ->binf(), or ->binf('-').Photochromy
I was more concern with cases where someone turned on bigint and you didn't notice. :)Ghana
-1 because any copy/pasted solution is bad. Use the CPAN. See my answer.Tonguelashing
@Tonguelashing Such a fundamental check should be possible without using the CPAN. It should be a core functionality.Abiogenetic
O
15
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.

Oenone answered 27/7, 2009 at 9:8 Comment(6)
What if $a is not a number, but is actually the string "nan"?Windfall
@Ryan: String "nan" is not a number of course. ysth's solution works exactly same. Check perl -le 'sub isnan { ! defined( $_[0] <=> 9**9**9 ) }; print isnan("nan")' if you don't trust me.Oenone
Is Inf isn't quite perfect, you also want to check for '-inf'.Maness
You need to apply the lc operator to $a before comparing, because the actual strings are things like "Inf" rather than "inf".Otter
It seems that it changed in the last 11 years.Oenone
different platforms can differPhotochromy
I
7

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?

Internist answered 29/7, 2009 at 8:50 Comment(0)
T
7

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.

Tonguelashing answered 20/3, 2013 at 14:58 Comment(1)
It should be mentioned that Data::Floats check for NaN is dependent on NaN support on the platform.Paganism
F
0

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 '=='

Fellers answered 7/6, 2016 at 19:3 Comment(0)
O
0
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
Ostracoderm answered 14/3 at 12:7 Comment(2)
pretty sure some platforms have INF or Inf, not inf. why the $ anchor but no starting ^ anchor?Photochromy
On missing ^ : 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
V
-2

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")}
Viceregent answered 19/10, 2012 at 17:32 Comment(6)
(1) and (3) are no different than just $NAN = "NAN"+1;, $INF = "INF"+1; $INFN = -"INF"; just more verbosePhotochromy
oh, I see (4) uses barewords too, only without disabling strict. And all of these that rely on strings like "NAN" and "INF" becoming the appropriate "number" in numeric context will fail on older perls or where the C runtime doesn't support it (e.g. strawberry perl or activeperl on windows)Photochromy
A comprehensive answer would include a testsuite.Tonguelashing
Changed it to be more clear... it's a succinct, correct answer. Also works on activeperl/windows, etc.Viceregent
You don't need the 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
i removed all the barewordsViceregent

© 2022 - 2024 — McMap. All rights reserved.