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

I have noticed that the largest integer I can safely work with is 9007199254740991 in 32-bit environment. And that's because the numbers are stored in IEEE-754 format internally. Whatever that means. Anyway, my question is if Perl is compiled for a 64-bit machine and supports 64-bit integer operations, does it still store numbers same way internally, or is there something totally different in place for that? Because I would like to know what's the largest integer I can safely work with in a 64-bit environment.

Here is a simple example:

my $MAXQUAD = 18446744073709551615; # equals 0xffffffffffffffff for (my $i = 0; $i <= $MAXQUAD; $i++) { printf("\n %f", $i); } exit;

Please ignore the fact that this will take forever to count from 0 to $MAXQUAD, but would it actually work? ...because in a 32-bit environment, this loop will never end! It will count from 0 to 9007199254740992, and it will not increment beyond that. It will just get stuck at that number.

Replies are listed 'Best First'.
Re: Largest integer in 64-bit perl
by NERDVANA (Priest) on May 16, 2025 at 18:48 UTC
    Perl has three internal numeric types, which vary based on how Perl was compiled.
    • IV - an "integer value" which can be 32-bit or 64-bit. It can optionally be 64-bit on 32-bit systems (for a small performance hit) but it will always be 64-bit on 64-bit systems. It will have a maximum value of 2^31-1 or 2^63-1 because of one bit being lost for twos-complement negative numbers.
    • UV - an "unsigned value" which likewise can be 32-bit or 64-bit, but lets you go up to the full 2^64-1 maximum value. This does not let your numbers go more negative, only more positive.
    • NV - either a 64-bit floating point (IEEE-754) or 80-bit floating point. 64-bit is much faster so most systems compile it with that. 64-bit floats can hold up to 53-bit integers. 80-bit floats can hold up to 64-bit integers.

    So, on a 32-bit system configured for 32-bit IV, the 64-bit NV lets you store larger integers in a float even though NV is meant to be used for fractional values. On a 64-bit system, the IV is always 64-bit so there's no advantage to using NVs to store integers.

    You can check the details of how your perl was compiled using the Config module.

    perl -E 'use Config; say $Config{ivsize}; say $Config{nvsize}'

    FWIW, the only 32-bit system I still have access to has a perl compiled with 64-bit IV.

      I'm wondering:

      What are the cases where Perl decides to use UV for a variable, i.e. negatives are ruled out?

      or is this only for internal use, like array indices?

      Update

      To answer my own question, bitwise operations won't like a sign. (Doh!)

      FWIW: perlnumber only mentions this indirectly, for the exception made with use integer

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      see Wikisyntax for the Monastery

Re: Largest integer in 64-bit perl
by syphilis (Archbishop) on May 17, 2025 at 02:19 UTC
    Anyway, my question is if Perl is compiled for a 64-bit machine and supports 64-bit integer operations, does it still store numbers same way internally

    It's still essentially the same.
    Your perl can exactly represent all integers in the range -9007199254740992..9007199254740992.
    Integer values in the range -2147483647..4294967295 can be stored as 32-bit integer type (IV), the rest can only be stored as 53-bit (double) precision float type (NV).

    If you go to a 64-bit perl that has the double precision NV type, then the range increases to -9223372036854775808..18446744073709551616. All of these integer values (except for 18446744073709551616 and -9223372036854775808, which can only be stored as an NV) can be stored as the integer type (IV). Not all all of them are representable as an NV.

    If your perl has an nvtype of 64-bit precision long double, then the range expands to -18446744073709551616..18446744073709551616.
    And if your perl has an nvtype of __float128 (or 113-bit precision long double), then the range is -10384593717069655257060992658440192..10384593717069655257060992658440192.

    As you can see, except for the 64-bit perl with the 53-bit NV, the range is dependent upon on the NV type, not the IV type.

    Cheers,
    Rob
Re: Largest integer in 64-bit perl
by ikegami (Patriarch) on May 17, 2025 at 15:15 UTC

    I have noticed that the largest integer I can safely work with is 9007199254740991 in 32-bit environment

    You're off by one. On a machine with 32-bit integers and 64-bit IEEE floats, you can actually reach 9,007,199,254,740,992 (but not 9,007,199,254,740,993).

    I would like to know what's the largest integer I can safely work with in a 64-bit environment.

    You're also off by one for a machine with 64-bit integers and 64-bit IEEE floats. You can actually reach 18,446,744,073,709,551,616 (but not 18,446,744,073,709,551,617).

    I've previously answered your question on StackOverflow, which I'll copy below.



    The following is a general solution that will obtain the value for any build of Perl:

    use Config qw( %Config ); use List::Util qw( max ); my $max = max( ~0+1, eval( $Config{ nv_overflows_integers_at } ) );

    A detailed explanation follows.


    "Integer" can refer to a family of data types (int16_t, uint32_t, etc). There's no gap in the numbers these can represent.

    "Integer" can also refer to numbers without a fractional component, regardless of the type of the variable used to store it. ++ will seamlessly transition between data types, so this is what's relevant to this question.

    Floating point numbers can store integers in this sense, and it's possible to store very large numbers as floats without being able to add one to them. The reason for this is that floating pointer numbers are stored using the following form:

    [+/-]1._____..._____ * 2**____

    For example, let's say the mantissa of your floats can store 52 bits after the decimal, and you want to add 1 to 2**53.

    __52 bits__ / \ 1.00000...00000 * 2**53 Large power of two + 1.00000...00000 * 2**0 1 -------------------------- 1.00000...00000 * 2**53 + 0.00000...000001 * 2**53 Normalized exponents -------------------------- 1.00000...000001 * 2**53 But we have limited number of bits -------------------------- 1.00000...00000 * 2**53 Original large power of two

    So it is possible to hit a gap when using floating point numbers. However, you started with a number stored as signed integer.

    $ perl -MB=svref_2object,SVf_IVisUV,SVf_NOK -e' $i = 0; $sv = svref_2object(\$i); print $sv->FLAGS & SVf_NOK ? "NV\n" # Float : $sv->FLAGS & SVf_IVisUV ? "UV\n" # Unsigned int : "IV\n"; # Signed int ' IV

    ++$i will leave the number as a signed integer value ("IV") until it cannot anymore. At that point, it will start using an unsigned integer values ("UV").

    $ perl -MConfig -MB=svref_2object,SVf_IVisUV,SVf_NOK -e' $i = hex("7F".("FF"x($Config{ivsize}-2))."FD"); $sv = svref_2object(\$i); for (1..4) { ++$i; printf $sv->FLAGS & SVf_NOK ? "NV %.0f\n" : $sv->FLAGS & SVf_IVisUV ? "UV %u\n" : "IV %d\n", $i; } ' IV 2147483646 IV 2147483647 <-- 2**31 - 1 Largest IV UV 2147483648 UV 2147483649

    or

    IV 9223372036854775806 IV 9223372036854775807 <-- 2**63 - 1 Largest IV UV 9223372036854775808 UV 9223372036854775809

    Still no gap because no floating point numbers have been used yet. But Perl will eventually use floating point numbers ("NV") because they have a far larger range than integers. ++$i will switch to using a floating point number when it runs out of unsigned integers.

    When that happens depends on your build of Perl. Not all builds of Perl have the same integer and floating point number sizes.

    On one machine:

    $ perl -V:[in]vsize ivsize='4'; # 32-bit integers nvsize='8'; # 64-bit floats

    On another:

    $ perl -V:[in]vsize ivsize='8'; # 64-bit integers nvsize='8'; # 64-bit floats

    On a system where nvsize is larger than ivsize

    On these systems, the first gap will happen above the largest unsigned integer. If your system uses IEEE double-precision floats, your floats have 53-bit of precision. They can represent without loss all integers from -253 to 253 (inclusive). ++ will fail to increment beyond that.

    $ perl -MConfig -MB=svref_2object,SVf_IVisUV,SVf_NOK -e' $i = eval($Config{nv_overflows_integers_at}) - 3; $sv = svref_2object(\$i); for (1..4) { ++$i; printf $sv->FLAGS & SVf_NOK ? "NV %.0f\n" : $sv->FLAGS & SVf_IVisUV ? "UV %u\n" : "IV %d\n", $i; } ' NV 9007199254740990 NV 9007199254740991 Precision required as a float: NV 9007199254740992 <-- 2**53 1 bit NV 9007199254740992 <-- 2**53 + 1 54 bits, but only 53 are available

    On a system where nvsize is no larger than ivsize

    On these systems, the first gap will happen before the largest unsigned integer. Switching to floating pointer numbers will allow you to go one further (a large power of two), but that's it. ++ will fail to increment beyond the largest unsigned integer + 1.

    $ perl -MConfig -MB=svref_2object,SVf_IVisUV,SVf_NOK -e' $i = hex(("FF"x($Config{ivsize}-1))."FD"); $sv = svref_2object(\$i); for (1..4) { ++$i; printf $sv->FLAGS & SVf_NOK ? "NV %.0f\n" : $sv->FLAGS & SVf_IVisUV ? "UV %u\n" : "IV %d\n", $i; } ' UV 18446744073709551614 UV 18446744073709551615 <-- 2**64 - 1 Largest UV NV 18446744073709551616 <-- 2**64 1 bit of precision required NV 18446744073709551616 <-- 2**64 + 1 65, but only 53 are available
Re: Largest integer in 64-bit perl
by pfaut (Priest) on May 16, 2025 at 18:06 UTC

    Perl may internally keep your numbers as floating point values unless you tell it not to. Add use integer; at the top of your program and see what happens.

    If you want to print integer values, use %d, not %f in your print format.

    A hex value of all 'F's represents a negative value (2's complement notation). I altered your max value to be the largest 64 bit positive number.

    use integer; my $MAXQUAD = 9223372036854775807; # equals 0x7fffffffffffffff for (my $i = 0; $i <= $MAXQUAD; $i++) { printf("\n %d", $i); } exit;
    90% of every Perl application is already written.
    dragonchild
      Interestingly, the "use integer" line did absolutely nothing for me on 32-bit Windows XP TinyPerl 5.8. I tried to run it on Linux with 64-bit Perl 5.36, and to my surprise, it didn't even run! :-O It produced no error message and no output at all. The program does have a for loop in it, but it looked like it didn't even start the loop. I don't understand... Here's the program again:

      #!/usr/bin/perl use strict; use warnings; use integer; $| = 1; my $MAXQUAD = 18446744073709551615; for (my $i = 9007199254740000; $i <= $MAXQUAD; $i++) { printf("\n %.0f %d", $i, $i); }

      So, anyway, I did notice that if I remove the "use integer" line, then the program runs. In fact, if I started the loop at, let's say, 4611686018420000000, then it will keep counting on the 64-bit Perl in Linux. Interesting!

      However, when I stop the program by pressing CTRL+C, I notice something else. When printing the number $i as float, it sort of loses precision, but when printing it as %d number, it prints it precisely. In TinyPerl, I had the opposite experience with this where %f printed more precise values than %d. This is so weird... :P

      4611686018420022272 4611686018420022338 4611686018420022272 4611686018420022339 4611686018420022272 4611686018420022340 4611686018420022272 4611686018420022341 4611686018420022272 4611686018420022342 4611686018420022272 4611686018420022343 4611686018420022272 4611686018420022344 4611686018420022272 4611686018420022345 4611686018420022272 4611686018420022346 4611686018420022272 4611686018420022347 4611686018420022272 4611686018420022348 4611686018420022272 4611686018420022349 4611686018420022272 4611686018420022350

        Try printing $MAXQUAD. I bet you get '-1'. That's why your code looks like it doesn't run.

        Update: I tried this and it prints the set value. However, I wonder if perl is treating it as a signed value when comparing it to $i because the loop never executes.

        90% of every Perl application is already written.
        dragonchild
Re: Largest integer in 64-bit perl
by jwkrahn (Abbot) on May 16, 2025 at 23:41 UTC
    $ perl -MPOSIX -le'print for CHAR_MAX, UCHAR_MAX, SHRT_MAX, USHRT_MAX, + INT_MAX, UINT_MAX, LONG_MAX, ULONG_MAX, FLT_MAX, DBL_MAX' 127 255 32767 65535 2147483647 4294967295 9223372036854775807 18446744073709551615 3.40282346638529e+38 1.79769313486232e+308
    Naked blocks are fun! -- Randal L. Schwartz, Perl hacker

      None of those give the desired value.

Re: Largest integer in 64-bit perl
by LanX (Saint) on May 16, 2025 at 18:39 UTC
    Your value is 2**53-1, not because it's the biggest 32bit Integer value, but because Perl automatically swaps to floating point with a 53 bit mantissa if the 32 bits are exceeded (see Double-precision floating-point format and perlnumber )

    The max value for 64 bit representation will be about 2**63-1, because you need one bit for the sign in signed integers.

    This has already been discussed many times.

    NB: all values probably off by one, (UPDATED -1 because one slot will be occupied by zero.)

    I'm not going to look up the correct values for you, if you're too lazy¹ to supersearch or Google, so am I.

    FWIW: Your linear search is not terribly clever, use a binary search and you'll have finished before pronouncing the s in stupid.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

    ¹) quote: > "stored in IEEE-754 format internally. Whatever that means."

      How would you use a binary search to find the first gap?

        I would try to test the OP's problem that predecessor or successor are identical in numerical comparison.

        use v5.12; use warnings; #say "Mode: ", my $mode = $ARGV[0] // 0; use Config; say $Config{ivsize}; say $Config{nvsize}; sub test { my ($mode) = @_; say "**** Mode: $mode"; my $now=1; for my $x ( 1.. 65) { $now = $mode == 0 ? 2 * $now : $mode == 1 ? 2 ** $x : $mode == 2 ? 2 ** $x -1 : die "Mode=$mode not implemented yet"; if ( $now == $now-1 or $now == $now+1 ) { print "Problem at $now = 2**$x\n"; printf "%22u %22u %22u\n",$now-1,$now,$now+1; last; } } } test($_) for 0..2;

        this will break at 2**64 when I stay purely integer

        NB: I didn't implement the full binary search, since there is no backtracking yet

        But I'm getting weird conversion results when leaving the realm of integer (see the "modes"), these are most likely side-effects of starting with a float (or bugs). Probably better to use Hex-strings.

        perl /home/lanx/perl/pm/integer-gap.pl 8 8 **** Mode: 0 Problem at 1.84467440737096e+19 = 2**64 18446744073709551615 18446744073709551615 18446744073709551615 **** Mode: 1 Problem at 9.00719925474099e+15 = 2**53 9007199254740991 9007199254740992 9007199254740993 **** Mode: 2 Problem at 4.61168601842739e+18 = 2**62 4611686018427387904 4611686018427387904 4611686018427387904

        YMMV...

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

        Update

        In hindsight, the cleanest approach would be to only rely on addition and substraction of integers. No multiplications or exponentions (which might be implemented as approximation)

        Since the last safe interval is guaranteed to be an integer too.

Re: Largest integer in 64-bit perl
by BillKSmith (Monsignor) on May 27, 2025 at 01:21 UTC
    I found this thread to be very interesting. It motivated me to read most of the references. My conclusion is that any application which needs integers even close to 2**53 probably should not be written in native Perl. Even if you can make it work, future changes are likely to introduce subtle bugs. Use either the pragma bigint or the module Math::BigInt. If execution time is a problem, consider using a language with typed variables such as 'C' or 'FORTRAN'.
    Bill

      What a weird lesson to take. You'll have "problems near 2**53" in those languages too! Only by using a 64-bit int can you go further. The difference is that Perl does this automatically! Perl is actually better at this.

        > The difference is that Perl does this automatically!

        Is a whole number at the extreme of a mantissa's precision really converted to 64 bit integer if incremented?

        My results from binary search indicated otherwise. This might also depend on the type of operation.

        Plus might do, mult might stick with float.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

        ”…weird lesson to take. You'll have "problems near 2**53" in those languages too!”

        I'm not so sure about that. Or at least I don't see the problems:

        use anyhow::{Context, Result}; use num::BigUint; fn frob(n: u64) -> Result<BigUint> { Ok((1..=n) .into_iter() .map(|i| BigUint::from(i)) .reduce(|a, b| a * b) .context("fuba")?) } fn main() -> Result<()> { println!("i8 {}", i8::MAX); println!("i16 {}", i16::MAX); println!("i32 {}", i32::MAX); println!("i64 {}", i64::MAX); println!("i128 {}", i128::MAX); println!("u8 {}", u8::MAX); println!("u16 {}", u16::MAX); println!("u32 {}", u32::MAX); println!("u64 {}", u64::MAX); println!("u128 {}", u128::MAX); println!("f32 {}", f32::MAX); println!("f64 {}", f64::MAX); println!("{}", frob(1000)?); Ok(()) }

        In any case, this seems to be a clearly defined matter in this case.

        When I referred to "Application which require integers ...", I assumed that the reason that they required integers was that they needed mathematically exact results. Yes, the typed languages usually require you to explicitly specify an appropriate integer type. I still believe that my conclusion holds in this case.
        Bill
Re: Largest integer in 64-bit perl
by Anonymous Monk on May 16, 2025 at 18:16 UTC