http://qs321.pair.com?node_id=11118680


in reply to Re^4: Reliably parsing an integer
in thread Reliably parsing an integer

Create the largest integer number that the current Perl interpreter supports.

If you aren't picking a specific maximum number but just want to allow whatever the running perl does then you just need a function like this valid_int here:

use strict; use warnings; use Test::More tests => 5; ok valid_int ('18446744073709551614'), '1 under max int'; ok valid_int ('18446744073709551615'), 'exactly max int'; ok ! valid_int ('18446744073709551616'), '1 over max int'; ok ! valid_int ('foo'), 'NaN'; ok ! valid_int ('1.3'), 'float'; sub valid_int { my $num = shift; return unless $num =~ /^\d+$/a; return int $num eq $num; }

The hard-coded values are just to test because I am on a 64-bit perl, adjust them to your own testing environment as appropriate.

Replies are listed 'Best First'.
Re^6: Reliably parsing an integer
by rdiez (Acolyte) on Jun 30, 2020 at 08:21 UTC

    Your solution is interesting, thanks.

    The trouble is, I do not really trust the "int $num eq $num;" expression. I have seen that 'int' can do weird things, and I am not convinced that there is no floating-point corner case somewhere on some hardware that spits out something that looks like an integer, because some inaccuracy cancels out when generating a string from it.

    Besides, the "eq" is actually converting the integer back to a string, which will cost performance. I think I can write faster code with the steps I outlined above. Stay tuned.

      I have seen that 'int' can do weird things, and I am not convinced that there is no floating-point corner case somewhere on some hardware that spits out something that looks like an integer, because some inaccuracy cancels out when generating a string from it.

      Of course, if you could show an example of this, that'd be much better than a vauge worry. int is documented to return an integer, and hippo's valid_int includes a check that the input is not floating-point.

      If you're worried about a cutoff happening at 9,007,199,254,740,991 instead of 9,007,199,254,740,992, both of which are over nine quadrillion, then I suggest you're worrying about the wrong thing: since you're saying you want this to be portable to different machines and different Perls, this cutoff is arbitrary anyway!

      If you want a precise cutoff, you should choose a specific one that you are pretty certain will work on all expected architectures, like say 2**31-1, and if you wanted to code super defensively, you can even compare this cutoff to the integer limits I linked to and report an error otherwise.

      Besides, the "eq" is actually converting the integer back to a string, which will cost performance.

      hippo beat me to it: Perl's internal conversions are very fast, I suspect it'll be negligible. But let's not guess - Benchmark!

      A reply falls below the community's threshold of quality. You may see it by logging in.
      Besides, the "eq" is actually converting the integer back to a string, which will cost performance.
      #!/usr/bin/env perl use strict; use warnings; use Benchmark 'timethis'; timethis (10_000_000, "valid_int ('18446744073709551614')"); sub valid_int { my $num = shift; return unless $num =~ /^\d+$/a; return int $num eq $num; } __END__ timethis 10000000: 8 wallclock secs ( 7.93 usr + 0.00 sys = 7.93 CP +U) @ 1261034.05/s (n=10000000)

      So, less than a microsecond on my aging system. Doubtless this can be improved upon (although about half the time taken appears to be the regex so there's a limit there too).

          A reply falls below the community's threshold of quality. You may see it by logging in.