What is the best way to check for infinity in a Perl module?
Asked Answered
P

1

13

In one of my modules, I have to deal with the concept of infinity. To date, I have been using 9**9**9 as positive infinity, and this seems to work well, is fast, and seems to be what perl's internals use as infinity.

However, things get a bit dicey if a user of my module decides to use one of the big number modules (like use bigint;), and then they use inf or Math::BigInt->binf() to represent infinity.

In some places it seems to work fine, but in others, comparisons that should be true or should be false end up the wrong way round leading to difficult to track down bugs.

I would like to support the various other notions of infinity with something that will work with both normal perl numbers, and arbitrary precision numbers.

But I also have concerns about performance since some of my comparisons to infinity occur in tight inner loops. Obviously inf from Math::BigInt is going to be slower than 9**9**9 (due to either calling tied or overloaded methods on each access). Has anyone dealt with this problem in the past? If so, what was your solution?

I've thought about using my own constant for infinity, defined something like this:

use constant INF => if_any_bignum_modules_loaded() 
                    ? Math::BigInt->binf 
                    : 9**9**9;

And then adding the caveat to my module that any bignum modules should be loaded first. Does this sound sensible? Is there a reliable implementation of if_any_bignum... out there, or should I roll my own?

Perisarc answered 4/10, 2010 at 16:0 Comment(4)
possible duplicate of How do I create or test for NaN or infinity in Perl?Principled
@Principled => please read the question before voting to close, none of the answers in that question cover this question...Perisarc
ok, that wasn't very clear, as the questions are identical.Principled
@Principled => this question has to do with how to best support end users who may or may not be using bignums, without imposing a performance hit on users who aren't. The linked question briefly touches upon the issues related to bignums in one of the comments, but the analysis is terse, incomplete, and does not provide a solution. I looked at that question and everything else that comes up in a search for [perl] infinity before posting this question.Perisarc
C
10

Math::BigInt provides an is_inf method. It can detect infinity for both regular Perl numbers, including Perl's built-in inf, such as return by 9**9**9, as well as any sort of Math::Big* instance or those magic thingies you get when you're using bigint. Loading Math::BigInt comes with barely any overhead at all - none comparable to using bigint anyway - and is a core module since the very beginning of perl 5.

use 5.010;
use Math::BigInt;

say Math::BigInt->is_inf(42);
say Math::BigInt->is_inf(9**9**9);
say Math::BigInt->is_inf(Math::BigInt->binf);

__END__
0
1
1

You might also want to have a look at the implementation of that method if you really wanted to avoid loading Math::BigInt at all. It's easy enough to inline into other code with just slight modifications, although I would really recommend just using the functionality from the module directly.

Carmelacarmelia answered 4/10, 2010 at 16:26 Comment(4)
This definitely looks like a good catch-all solution for all of the non-inner loop tests. I will have to benchmark to see the performance impact on the inner loops.Perisarc
If you find it's too slow for whatever you're doing and figure out a way to implement the same functionality in a faster way, I'd be very happy to apply your patches to Math::BigInt and ship them to CPAN.Carmelacarmelia
Sounds good, I will see what I can do. I'd imagine changing all of the regex matches into calls to index would be a start.Perisarc
=> inf_nan.t needs another test, Math::BigInt->is_inf(9**9**9) seems to fail with strawberry perl at least on a 64-bit platform. the underlying clib returns 1.#INF for 9**9**9 which BigInt is mapping to NaN. same result on 5.8.9 and 5.12Perisarc

© 2022 - 2024 — McMap. All rights reserved.