harangzsolt33 has asked for the wisdom of the Perl Monks concerning the following question:

Can somebody explain to me how to do 32-bit operations in Perl? I thought I knew this, but apparently perl works differently than what I think would be logical! I just want to add some big 32-bit numbers, which will overflow, of course. But I don't care about the 33rd bit and anything above that. I don't need them. I just want to get the first 32 bits. But everytime I try this, I either get weird results or -1 which is unexplainable!!

The result of both of these calculations below should be : 0x0000000133000000 which equals 5,150,605,312. Now, since I'm using a 32-bit software, the result ought to be 0x33000000 which equals 855,638,016. But I get neither of those!!! Why?

printf("\nLINE 1: 855638016 == %d", 0x34000000 + 0xFF000000);

print "\nLINE 2: 855638016 == ", (872415232 + 4278190080) & 0xFFFFFFFF;

This is what TinyPerl 5.8 on WinXP prints on my computer screen:

LINE 1: 855638016 == -1
LINE 2: 855638016 == 4294967295

(Notice that both of these numbers are the same thing. -1 = 0xFFFFFFFF = 4294967295. But on the first line, I get -1, and on the second line I get the positive number in base 10. This is totally unacceptable! What is going on here???

Replies are listed 'Best First'.
Re: perl arithmetic is killing me! HELP!
by syphilis (Archbishop) on Nov 07, 2018 at 12:39 UTC
    Can somebody explain to me how to do 32-bit operations in Perl?

    I wouldn't be very comfortable doing 32-bit unsigned integer operations in perl if overflows can occur. I'd probably do it in C and access the operations from perl via Inline::C or XS.
    As already mentioned, 'use integer', is probably of value though I wouldn't like to vouch for its reliability - especially on a perl version as old as 5.8.

    I don't know what "TinyPerl" is - could you provide the output of perl -V (that's an uppercase "V").

    But on the first line, I get -1, and on the second line I get the positive number in base 10

    As you've noted, "-1" and "4294967925" are the same 32-bit integer.
    On the first line you asked for it to be expressed as a signed value (ie -1). On the second line you got to see the unsigned value (4294967295).
    It seems to me that since you're actually interested in the unsigned values, the first line should have specified "%u" instead of "%d".

    But, of course, the value you really expected to see was 855638016, for which you need to satisfy 2 requirements:
    1) That you 'use integer'
    2) That the size of your perl's integer is 4 bytes (check perl -V:ivsize).

    With both of those conditions satisfied I get:
    LINE 1: 855638016 == 855638016 LINE 2: 855638016 == 855638016
    But if perl's integer is 8 bytes then, irrespective of whether I 'use integer', I get:
    LINE 1: 855638016 == 5150605312 LINE 2: 855638016 == 855638016
    Note that, in this instance, the "%d" in "LINE 1" is providing a signed 64-bit value because the perl integer is a 64-bit type.

    Cheers,
    Rob

      The actual requirements:

      • That you 'use integer'
      • That the size of your perl's integer is 4 bytes (check perl -V:ivsize) or that you use & 0xFFFF_FFFF.
        The actual requirements:
        •That you 'use integer'
        •That the size of your perl's integer is 4 bytes (check perl -V:ivsize) or that you use & 0xFFFF_FFFF


        I think there's at least one more condition, namely: Do not assign a value that's greater than UINT_MAX.

        I don't know what others expect to happen when, having chosen to 'use integer', one assigns (to a perl scalar) a value that is greater than UINT_MAX, but the result is one that I do not expect.
        This is behaviour that I do expect when ivsize is 4:
        C:\> perl -Minteger -wle "$x = 4294967295 + 3;print $x;" 2
        And I expect the same output when I do:
        C:\> perl -Minteger -wle "$x = 4294967298;print $x;" 4294967298
        Alas, such handling of this corner case leaves me feeling uneasy about using the integer pragma at all.

        How many other surprises (or traps for the unwary) are to be found in the finer details of this pragma's behaviour ?

        Cheers,
        Rob
Re: perl arithmetic is killing me! HELP!
by ikegami (Patriarch) on Nov 07, 2018 at 13:30 UTC

    & only works on numbers that fit in an IV (a signed integer type) or UV (an unsigned integer type). This range is -2,147,483,648..4,294,967,295 for you. Without use integer;, 872_415_232 + 4_278_190_080 produces a number larger than that, so & produces junk. Specifically, it returns the largest UV (4,294,967,295 for you), which %d casts to an IV (-1).

      Got it! Thanks. But you know, in many other programming languages, it doesn't work like that. For example, both JavaScript and Perl use 64-bit IEEE-754 double precision floats for numbers. So, doing the same complex calculation in Perl and JavaScript, one should get the SAME RESULT. That was my assumption. But that's not the reality.

        Perl is closer to C than to JavaScript.

        $ cat a.c int main() { double x = 1; double y = 1; int z = x & y; return 0; } $ gcc -Wall -Wextra -pedantic a.c -o a && a a.c: In function ‘main’: a.c:4:15: error: invalid operands to binary & (have ‘double’ and ‘doub +le’) int z = x & y; ^ a.c:4:9: warning: unused variable ‘z’ [-Wunused-variable] int z = x & y; ^

        And the underlying reason for that is that CPUs simply can't perform bitwise-AND on floats (because it makes little sense to do so).

Re: perl arithmetic is killing me! HELP!
by LanX (Saint) on Nov 06, 2018 at 23:12 UTC
    > What is going on here???

    from sprintf :

    • %d a signed integer, in decimal .

    > This is totally unacceptable!

    RTFM? ;-p

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

    update

    have no 32 bit but this works for me:

    DB<4> printf "\nLINE 1: 855638016 == %x", (0x34000000 + 0xFF000000) +& 0xFFFFFFFF; LINE 1: 855638016 == 33000000 DB<5>
Re: perl arithmetic is killing me! HELP!
by Anonymous Monk on Nov 06, 2018 at 23:10 UTC
    cause Perl numbers arent integers! they magically become floats when they need to. try use integer;
      Wait. You mean they magically become integers when they need to. Right? Because, what I've noticed here is that they magically become -1 when Perl runs out of tricks! That is when I put a # in front of the "use integer;" line. Please look at my code below:
      use strict; use warnings; use integer; my $string = 'Hello World!'; my $x = Checksum($string); printf("\n\nChecksum = %0.8X\n", $x); exit; # # This function returns a 32-bit integer. # # Usage: LONG = Checksum(STRING) # sub Checksum { my $C = 0x55555555; # Starting value. # Make sure we got a valid string argument. @_ or return $C; my $S = shift; defined $S or return $C; # Start the loop with the # last character of the string: my $i = length($S); my $V; my $H; my $L; while ($i--) { # $V contains the character code shifted # to the left by either 0, 8, 16, or 24 bits: $V = ((vec($S, $i, 8) + 1) << (($i & 3) << 3)); # We add this value to the checksum $C $C += $V; # Then we rotate this 32-bit "checksum" to # the left by 1. There is no ROL operator # in Perl, so we take the 32nd bit ($H) and # then we shift the rest to the left, and # finally we OR the low and high bits # together to achieve the desired rotation. # So, it would look something like this: # 01110101011111111101000001110000 <<< # 11101010111111111010000011100000 <<< # 11010101111111110100000111000001 <<< # 10101011111111101000001110000011 <<< # 01010111111111010000011100000111 <<< # 10101111111110100000111000001110 <<< # 01011111111101000001110000011101 <<< # 10111111111010000011100000111010 <<< # 01111111110100000111000001110101 <<< $H = (($C >> 31) & 1); $L = (($C << 1) & 0xFFFFFFFE); $C = $H | $L; # Now, here we print a snapshot of $V # and we print the high bit and the low # portion of the integer: printf("\nV=%0.8X \tH=$H L=%0.32b", $V, $L); } # Make sure we return a 32-bit integer return $C & 0xFFFFFFFF; }