Skip to content

← Values, Types, Initialisation step 1 of 4

Easy Primitives

int is not a width

How many bits is an int?

The standard’s answer is “at least 16”. In practice it is 32 nearly everywhere you will run, which is exactly what makes this dangerous: the bug cannot be reproduced on your machine, only on someone else’s.

What C++ actually guarantees is an ordering, not a size:

sizeof(char) == 1  <=  sizeof(short)  <=  sizeof(int)
                   <=  sizeof(long)   <=  sizeof(long long)

And long is the one that bites. On 64-bit Linux and macOS it is 8 bytes; on 64-bit Windows it is 4. Identical source, identical compiler version, different answer — which is why long should essentially never appear in code you intend to be portable.

The fixed-width types

<cstdint> gives you the sizes you meant to say:

std::int8_t   std::int16_t   std::int32_t   std::int64_t
std::uint8_t  std::uint16_t  std::uint32_t  std::uint64_t

These are exact. std::int64_t is 64 bits on every platform that has the type at all, and a wrap that happens on your machine happens identically on every other.

When to use which

Not “always use fixed width” — that would be a rule you would resent.

  • A value that must not overflow at a known magnitude → fixed width. Byte counts, file offsets, ids, checksums, anything with a protocol or a file format behind it.
  • An index into a containerstd::size_t, which is what .size() returns and what operator[] takes.
  • Arithmetic on small local valuesint is fine and idiomatic. Nobody writes std::int32_t i = 0; in a three-line loop.

The line is roughly: int inside a function, fixed width at a boundary. If the value crosses into a struct, a file, a socket or another team’s code, say how wide it is.

Your task

std::int64_t checksum(const std::vector<std::int64_t>& values);

Return the sum of values, computed so that it wraps as a 64-bit unsigned value and is then reinterpreted as signed — the ordinary shape of a rolling checksum, and the reason this problem is about width at all.

Concretely: accumulate into a std::uint64_t, which is defined to wrap modulo 2^64, then convert the result back with static_cast.

Signed overflow is undefined behaviour in C++ — not “wraps”, not “implementation-defined”, but undefined, and optimisers act on that. Unsigned overflow is fully defined and wraps. That difference is the reason the accumulator has to be unsigned even though the answer is signed, and it is the single most practically important thing in this track.

The starter uses int. On the visible tests it is right. On one of the hidden tests it is not.