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

I wrote a program to calculate the range of unsigned integers one could get from a certain number of bits (from 1 to 32).

(Actually, it was a problem I found in KDevelop, and the original solution was supposed to be writ up in C. I thought I'd try my hand at doing it in Perl. The problem read:


" 'for' problem. Count from 1 to 32 and list the range of unsigned integer numbers that can be stored in each collection of bits. I.E 8 bits can hold the range 0 to 255." )

So i wrote this program:

#!/usr/bin/perl -l #Program to calculate the range of unsigned integer numbers #produced from 1 to 32 bits. use warnings; use strict; for(0..32){ my $bin='0b'.'1'x$_; print "$_ : 0 to ",oct $bin; }

and it worked great. But then I tried the C solution provided with KDevelop:

#include <stdio.h> #include <stdlib.h> main () { long int i=1,j=i; /* title */ puts("Bits\tRange"); puts("----\t-----"); /* O/P data and calc the next * set of values */ for (i=1; i<=32; i++) { printf("%2ld\t0-%12lu\n", i, j*=2); } }

but it printed 0-0 as the range for 64 bits. I'm guessing there was a buffer overflow at 64 bits (?). (btw, for the pedantic, the program doesn't work correctly, and you must change j*=2 to (j*=2)-1 for it to).

So i changed the for loop in my script above to:

for(31..64){

to see if Perl would be so affected. It worked OK! But it produced a slew of error messages claiming:

Binary number > 0b11111111111111111111111111111111 non-portable at ./b +its line 11

So I included a use diagnostics at the top of my code, adn got a helpful error message, explaining everything:

Integer overflow in binary number at ./bits line 12 (#1) (W overflow) The hexadecimal, octal or binary number you have spec +ified either as a literal or as an argument to hex() or oct() is too big + for your architecture, and has been converted to a floating point numb +er. On a 32-bit architecture the largest hexadecimal, octal or binary +number representable without overflow is 0xFFFFFFFF, 037777777777, or 0b11111111111111111111111111111111 respectively. Note that Perl transparently promotes all numbers to a floating point representat +ion internally--subject to loss of precision errors in subsequent operations.

And, a bit down the line:

Binary number > 0b11111111111111111111111111111111 non-portable at ./b +its line 12 (#2) (W portable) The binary number you specified is larger than 2**32- +1 (4294967295) and therefore non-portable between systems. See perlport for more on portability concerns.

That cleared up why the error messages were coming up.

So, a few questions:

  1. Does C or any other languagae have perl's (helpful) ability to convert numbers when there is a danger of them overflowing?
  2. If perl implementations all have the same ability to prevent overflows this way, why is this a portability problem?
  3. Are there any other (neater) implementations of my script above?

Thanks

janitored by ybiC: Retitle from "Buffer overflow: Perl vs C" as per consideration process, add balanced <readmore> tags

Replies are listed 'Best First'.
Re: Integer overflow in Perl and C
by BrowserUk (Patriarch) on Jan 24, 2004 at 12:32 UTC

    1) REXX also treats numbers as numbers, not differenciating between integer and FP.

    2) Once your integer grow beyond 53-bits, you will start to loose precision in the conversion to FP.

    print $_, ': ', 2**$_ == 2**$_ -1 for 33 .. 64; 33 : 34 : 35 : 36 : 37 : 38 : 39 : 40 : 41 : 42 : 43 : 44 : 45 : 46 : 47 : 48 : 49 : 50 : 51 : 52 : 53 : 54 : 1 55 : 1 56 : 1 57 : 1 58 : 1 59 : 1 60 : 1 61 : 1 62 : 1 63 : 1 64 : 1

    On a 64-bit OS, this would not be the case as there would be no need to premote the number to a floating point. Equally, any OS/CRT/FPP that used 80-bit IEEE floating point instead of 64-bit would get a different set of results again--hence non-portable.

    3) Depends...


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    Timing (and a little luck) are everything!

Re: Integer overflow in Perl and C
by rob_au (Abbot) on Jan 24, 2004 at 12:21 UTC
    3. Are there any other (neater) implementations of my script above

    In terms of the original problem as presented, I believe that it an alternate approach employing the exponentiation operator/functions would be more efficient and side-step the format conversion problems.

    In Perl:

    perl -le 'print $_, ": 0 to ", 2**$_ - 1 for 0..32'
    In C:
    #include <stdio.h> #include <math.h> int main (void) { int i; for (i = 0; i <= 32; i++) printf("%d: 0 to %.f\n", i, pow(2, i) - 1); return(0); }
    Both implementations are capable of outputting ranges of unsigned integer numbers for up to 64 bits (untested beyond).

     

    perl -le "print unpack'N', pack'B32', '00000000000000000000001010110011'"

Re: Integer overflow in Perl and C
by Anonymous Monk on Jan 24, 2004 at 12:43 UTC