2

In a code I have been editing the previous programmer used a shift operator to add a moderately large number to a size_t integer. When I played with this particular integer for debugging purposes I found that changing the number did not yield predicable results.

Input:

std::size_t
    foo1 = 100000 << 20,
    foo2 = 200000 << 20,
    foo3 = 300000 << 20,
    foo4 = 400000 << 20;
std::cout << "foos1-4:";
std::cout << foo1;
std::cout << foo2;
std::cout << foo3;
std::cout << foo4;

yields:

foos1-4:
1778384896
18446744072971354112
1040187392
18446744072233156608

I'm know it is some kind of overflow error, but (to my admittedly limited knowledge) size_t is not supposed to have those. From what I understand size_t is an unsigned integer type that is able to hold a virtually unlimited number of integers.

From what I understand of bit-shift operators this code should be multiplying the number by 2^20 (1048576). Links to other pages on this site: What are bitwise shift (bit-shift) operators and how do they work?

Note - I worked out by hand that foo1 appears to be an overflow error with a 32 binary digit truncation, but all the others seem totally random to me.

From http://en.cppreference.com/w/cpp/types/size_t: std::size_t can store the maximum size of a theoretically possible object of any type (including array). From that I assume the issue must be in either how the integers are declared or how bit shift is operating.

What's going on?

Community
  • 1
  • 1
Rampallian
  • 123
  • 3
  • `size_t` is not "virtually unlimited"; it's simply guaranteed to be large enough to indicate the size *in bytes* of arbitrary objects. In practice, [the implementation used to compile the example code on cppreference has a maximum value of `18446744073709551615`](http://en.cppreference.com/w/cpp/types/numeric_limits/max#Example), but this is actually much larger than the required minimum. – Kyle Strand Sep 15 '16 at 22:50

1 Answers1

11

The problem isn't std::size_t but the int literals used. You can make them long enough using the UL postfix like here:

#include <iostream>

int main()
{
std::size_t
    foo1 = 100000UL << 20,
    foo2 = 200000UL << 20,
    foo3 = 300000UL << 20,
    foo4 = 400000UL << 20;
std::cout << "foos1-4:" << std::endl;
std::cout << foo1 << std::endl;
std::cout << foo2 << std::endl;
std::cout << foo3 << std::endl;
std::cout << foo4 << std::endl;
}

Output:

foos1-4:
104857600000
209715200000
314572800000
419430400000

Live Demo


Also note the compiler gives you a warning exactly about that:

main.cpp:6:19: warning: result of '(100000 << 20)' requires 38 bits to represent, but 'int' only has 32 bits [-Wshift-overflow=]
     foo1 = 100000 << 20,
            ~~~~~~~^~~~~
main.cpp:7:19: warning: result of '(200000 << 20)' requires 39 bits to represent, but 'int' only has 32 bits [-Wshift-overflow=]
     foo2 = 200000 << 20,
            ~~~~~~~^~~~~
main.cpp:8:19: warning: result of '(300000 << 20)' requires 40 bits to represent, but 'int' only has 32 bits [-Wshift-overflow=]
     foo3 = 300000 << 20,
            ~~~~~~~^~~~~
main.cpp:9:19: warning: result of '(400000 << 20)' requires 40 bits to represent, but 'int' only has 32 bits [-Wshift-overflow=]
     foo4 = 400000 << 20;
            ~~~~~~~^~~~~
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190