http://www.perlmonks.org?node_id=1010278


in reply to log() and int() problem

when using Data::Dumper you will see that $l is seen as a string not an integer

print "LOG      == ",$l,Dumper \$l,"\n";

prints

LOG      == 3$VAR1 = \'3';

As others pointed out log only returns floats up to certain precision, which IIRC also depends on the current implementation of Perl (that is compiling options). And internally floats are stored in the string-slot of a variable.

That means the slight calculation error might be too small to be shown by a print but is still sufficient to let == fail. Even if print would show the discrepancy, your approach wouldn't always work.

So better go the other way round and check if 125 == 5**$x with $x=int($l+$tolerance)

EDIT: The approach Moritz updated into his post is even better.

Cheers Rolf

Replies are listed 'Best First'.
Re^2: log() and int() problem
by dave_the_m (Monsignor) on Dec 25, 2012 at 18:34 UTC
    And internally floats are stored in the string-slot of a variable
    No they're not. An xpvnv has separate slots for an int, a float and a string:
    [davem@pigeon bleed]$ p -MDevel::Peek -e'$x=1; $x=2.2; $x = "three"; D +ump $x' SV = PVNV(0x1ae3150) at 0x1b021d0 REFCNT = 1 FLAGS = (POK,pPOK) IV = 1 NV = 2.2 PV = 0x1afb360 "three"\0 CUR = 5 LEN = 16

    Dave.

      OK, thanks for correcting!

      (I already suspected that I should place one more "IIRC". :)

      Maybe I remembered something different or Scalar::Util::dualvar confused me or the reality in perl is even more complex. (and the latter wouldn't surprise me)

      Cheers Rolf

      PS: or was it JS ... ?!?

      UPDATE:

      For the records, here the source of my misunderstanding

      from perlnumber:

      Perl can internally represent numbers in 3 different ways: as native integers, as native floating point numbers, and as decimal strings. Decimal strings may have an exponential notation part, as in "12.34e-56" . Native here means "a format supported by the C compiler which was used to build perl".

      emphasis added.

Re^2: log() and int() problem
by Anonymous Monk on Dec 25, 2012 at 21:03 UTC
    Thank you very much. I solved my issue with adding and subtracting 1 from the returned value.
    I think that perl should do this internally.
    my $l = log(125) / log(5); $l += 1; $l -= 1; print $l == int($l);

    Thank you all for the great answers.

      I solved my issue with adding and subtracting 1 from the returned value
      Sorry, but that's a terrible "solution". Your example floating point inaccuracy happens to be slightly greater than three. Since the int function truncates, consider what happens if the inaccuracy happens to be slightly less than three. Though a crude fix would be to add 0.5 (i.e. int($l+0.5) instead of int($l)), the perl documentation advises against using int for rounding and suggests sprintf and the POSIX floor and ceil functions as sounder alternatives.

      Still don't understand why you don't go with moritz's solution.

        > Though a crude fix would be to add 0.5 (i.e. int($l+0.5)

        I think you rather meant something like $ll=int($l+5e-15) =)

        (15 digits accuracy is just a guess)

        Cheers Rolf

      > I solved my issue

      you're very optimistic.

      I delved into Devel::Peek and I'm still not sure which type casting or crossed thresholds produced different results for 2 and 3.

      Anyway you're voyaging dangerous grounds, don't be surprised if your Float to Int conversions breaks again.

      Better rely on on already shown pure integer arithmetic for your integer (sic) tests. (rule of thumb!)

      Cheers Rolf

      PS:

      lanx@nc10-ubuntu:~$ perl -MDevel::Peek -e'$|=1;$e=2;$x=log(5**$e)/log( +5); Dump $x; print ("\n", ($x==$e) ? "" : "not " ,"equal\n\n"); Dump + $x;' SV = NV(0x9c86840) at 0x9c70f68 REFCNT = 1 FLAGS = (NOK,pNOK) NV = 2 equal SV = PVNV(0x9c50a60) at 0x9c70f68 REFCNT = 1 FLAGS = (IOK,NOK,pIOK,pNOK) IV = 2 NV = 2 PV = 0 lanx@nc10-ubuntu:~$ perl -MDevel::Peek -e'$|=1;$e=3;$x=log(5**$e)/log( +5); Dump $x; print ("\n", ($x==$e) ? "" : "not " ,"equal\n\n"); Dump + $x;' SV = NV(0x8fde840) at 0x8fc8f68 REFCNT = 1 FLAGS = (NOK,pNOK) NV = 3 not equal SV = PVNV(0x8fa8a60) at 0x8fc8f68 REFCNT = 1 FLAGS = (NOK,pIOK,pNOK) IV = 3 NV = 3 PV = 0

      UPDATE: improved code