in reply to &1 is no faster than %2 when checking for oddness. Oh well.

About all this entire thread has proven to me is that the difference is not significant, regardless of which one comes out on top. So I'll repeat myself here: choose the behaviour that says what you mean over the one that seems to be faster (today).

As tye pointed out, this type of optimisation can run you into loads of trouble if you forget about the range of your integers. However, if you stick to using % to determine "oddness", you're much more likely to stick within the range that perl supports. Getting it right for sum(1..n) when n gets up over 92_681, for example, may be important later. And because perl abstracts numbers that are too big to be held in an integer, the modulo continues to work.

Use & when you want to say "I'm twiddling bits". Use % when you want to say "I'm finding the remainder (if any)". Since the definition of odd (at least on the set of integers) is "not even" and the definition of even is "divisible by two with no remainder", it seems only logical to use % to find it.

Of course, it's also easily extensible to finding multiples (or not) of other numbers, too... Let the hardware guys worry about these optimisations. At some point, some guy at AMD or Intel or whomever will decide that evenness is important enough that when it seems a modulo operation where the divisor is 2 (or a power of 2), it switches to bit-twiddling. Even then, perl will still get it right for large numbers because it won't use the normal modulo operator when the numbers are too big, taking advantage of the hardware's ability to optimise normal numbers while not losing the accuracy of perl's abstractions.

  • Comment on Re: &1 is no faster than %2 when checking for oddness. Oh well.

Replies are listed 'Best First'.
Re^2: &1 is no faster than %2 when checking for oddness. Oh well.
by BrowserUk (Patriarch) on Nov 16, 2006 at 17:32 UTC

    I've reached different conclusions.

    • perl's implemetation of % is far from a perfect tool for deciding oddness of a 'number':
      Perl> print "$_: ", $_ % 2 for 1e308, 1e308 + 1, 1.999999999999999, 1.9999999999999999999;; 1e+308: 0 1e+308: 0 2: 1 2: 0

      There are dozens of other examples.

    • 'Oddness' is an inherently integer concept and a boolean condition. Using an inherently non-integer, non-boolean operator to test for an inherently integer, boolean condition is just plain wrong!

      Whilst reals can hold integer values and a much larger range of them than integers, but that does not mean using an inherently non-integer, non-boolean operator to test for an inherently integer, boolean condition is works.

      At the very least it has to be coded as if( ( $real % 2 ) == 1 ) {...};, but even that is fraught with the problems of floating point accuracy as shown above.

    • perl's attempt to abstract away different types of numbers, and transparently move between unsigned & signed integers and reals is far from complete. And at the margins, downright dangerous. In my book, this is unforgivable:
      use Devel::Peek;; $n = 9_999_999_998; print Dump( $n ); print $n & 1; print Dump( $n );; SV = NV(0x1844044) at 0x19920c4 REFCNT = 1 FLAGS = (NOK,pNOK) NV = 9999999998 1 SV = PVNV(0x1985a0c) at 0x19920c4 REFCNT = 1 FLAGS = (NOK,pIOK,pNOK,IsUV) UV = 4294967295 NV = 9999999998 PV = 0

      Silently permitting bit-wise boolean operations on numbers that are reals, and beyond the range of unsigned integers, is just plain weird.

    • Benchmarks make bad justifications for syntactic preferences.
    • Bad benchmarks are bad justifications for anything.
    • In the end, there is no abstraction that can beat the programmers knowledge of his applications needs.

      What if's about the future make bad justifications for anything.

      What if that was sum 1 .. 2**30;?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      If perl's abstraction of numbers isn't good enough for you, it's easy enough to fix. use bignum;.

      'Oddness' is an inherently integer concept and a boolean condition. Using an inherently non-integer, non-boolean operator to test for an inherently integer, boolean condition is just plain wrong!

      This I entirely, 100% agree with. Absolutely. However, I think you're getting a bit paranoid here. In my definition of oddness, I stipulated that it was confined to the set of integers - so talking about floating point concerns seems a bit orthogonal to that question. So, in the range of integers (from -inf to +inf), perl can't quite handle 'em all. But, that said, the concept itself is simple: an integer is odd iff it is not even. An integer is even iff its remainder when divided by two is zero. Thus, the simple boolean statement of $x % 2 != 0 (which is just a minor simplification from the more literal !($x % 2 == 0)) Or, a simpler definition of oddness is an integer which is not evenly divisible by two - which gives us the same boolean test. Then, when dealing with huge numbers, insert the bignum pragma, and you're done. A bit slow, perhaps, but accurate.

      As to your comments on bitwise operations on non-integral reals, I think that's just reminiscent of C where some people try to do that and think they're being smart. Instead of testing "if (x < 0)", they try to test the negative bit in the double, wherever that is. (Then they get hurt in a similar way when they somehow get a "negative zero".) Personally, I wouldn't mind a warning from perl's runtime for it - bit twiddling with reals is probably not what you were intending to do.

        If perl's abstraction of numbers isn't good enough for you,

        It's not "for me", it's "for my application".

        it's easy enough to fix. use bignum;.

        Ah! But ... if that's your answer to future proofing against speculative what-ifs

        1. You either
          • need to use bignum; in every program always; or

            Which is going to make your programs crawl when it is completely unnecessary 99.99% of the time.

          • selectively choose when to use bignum;

            Which is no different to using integers, and integer operators selectively; knowingly.

          You cannot have it both ways. What's good for the goose is good for the gander.

        2. You completely missed my point about sum 1 .. 2**30;, but that's my fault for not making it clear.

          Perl's reals are perfectly capable of holding sum 1 .. 2**30. There is no need for bignum.

          use List::Util qw[ sum ]; $n = 576460752840294400; printf "%20.f\n", $n;; 576460752840294400

          The problem is that trying to compute it (or even 1sum 1 .. 2**26 with List::Util::sum() crashes perl!

          It's a bug. But so is 99_999_999 & 1. No difference. Neither will allow you to future proof your application against speculative integer math that grows beyond certain limits.

          Then, when dealing with huge numbers, insert the bignum pragma, and you're done. A bit slow, perhaps, but accurate.

          When dealing with non-IV/UV values, don't use integer operations, and your done.

          You cannot have it both ways. What's good for the goose is good for the gander.


        I think you're getting a bit paranoid here. In my definition of oddness, I stipulated that it was confined to the set of integers - so talking about floating point concerns seems a bit orthogonal to that question

        I'm getting paranoid?.

        I was responding to your citing of tye (getting paranoid) about what happens when using & 1 on integers that have been promoted to reals through growing beyond the integer range.

        I was pointing out that using floats to hold oversized integers is at best a hack, and at worst dangerous because if you attempt to use integer operations on integers stored as floats, perl does very strange things.

        I was also pointing out that adding a second hack to work around the limitations of the first hack--using % 2 to test for oddness--was compounding a problem and producing another, worse problem. Especially, if you compound that by using if( $n % 2 ) ... instead of the full if( $n % 2 == 0 )....

        And that even if you do you the latter, you're still not "future proof", when your integers move beyond the range for which your reals are capable of representing integers accurately.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
      BrowserUk,
      What if that was sum 1 .. 2**30;?
      use Math::BigInt; my $sum = Math::BigInt->new(2); $sum->bpow(30); $sum = ($sum * $sum + $sum) / 2; print $sum; __DATA__ 576460752840294400
      This is mostly in jest but it does illustrate that programmer knowledge of the specific task at hand is more valuable then generalizations. There are similar formulas for summing just the odd or even numbers from 1 .. N as well.

      Cheers - L~R

        There is no need for Math::BigInt to sum 1 .. 2**30:

        $n = 576460752840294400; printf "%20.f\n", $n;; 576460752840294400

        The problem is that (on my system),

        use List::Util qw[ sum }; $n = sum 1 .. 2**26;

        crashes Perl.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.