Data Representation: Bits, Two's Complement, IEEE 754

কম্পিউটার আসলে কীভাবে ডেটা সংরক্ষণ করে

Read: ~40 min Intermediate 12 practice problems

1. The Universe Is Just Bits

To a computer, everything — numbers, text, images, audio, video — is just a sequence of bits: ones and zeros. The same 8 bits can mean a number, a character, a color, or a tiny part of a JPEG — it depends entirely on how the program interprets them.

কম্পিউটারের কাছে সবকিছু — সংখ্যা, লেখা, ছবি, ভিডিও — শুধুই বিট: ০ এবং ১। একই ৮ বিট কখনো সংখ্যা, কখনো অক্ষর, কখনো ছবির রঙ — সব নির্ভর করে প্রোগ্রাম কীভাবে সেগুলো ব্যাখ্যা করছে তার ওপর।

2. Binary, Decimal, Hex

DecimalBinary (8-bit)Hex
00000 00000x00
10000 00010x01
150000 11110x0F
160001 00000x10
2551111 11110xFF
Why hex? One hex digit = 4 bits. Two hex digits = 1 byte. Reading 0xFF is far easier than 11111111.

3. Signed Numbers — Two's Complement

How do you store -5 in bits? The answer C++ (and almost every CPU) uses is two's complement: invert the bits of the positive number and add 1.

Example for 8-bit +5 = 0000 0101  →  invert = 1111 1010  →  +1 = 1111 1011 = -5

Property: positive + (-positive) = 0 (with overflow). The arithmetic just works using ordinary binary addition.

8-bit signed range: -128 to +127. The leftmost bit acts as the sign bit (1 = negative).

4. Fixed-width Types — <cstdint>

int can be 16, 32, or 64 bits depending on the system — that's a portability problem. For exact sizes, use <cstdint>:

sizes.cpp
#include <iostream>
#include <cstdint>

int main() {
    std::cout << "int8_t  = " << sizeof(int8_t)  << " byte\n";
    std::cout << "int16_t = " << sizeof(int16_t) << " bytes\n";
    std::cout << "int32_t = " << sizeof(int32_t) << " bytes\n";
    std::cout << "int64_t = " << sizeof(int64_t) << " bytes\n";
    std::cout << "int     = " << sizeof(int)     << " bytes\n";
    std::cout << "long    = " << sizeof(long)    << " bytes\n";
    return 0;
}

5. Floating Point — IEEE 754

Real numbers (3.14, -0.001) use the IEEE 754 format. A 32-bit float has: 1 sign bit, 8 exponent bits, 23 mantissa bits. 64-bit double: 1 sign + 11 exponent + 52 mantissa.

The classic gotcha 0.1 + 0.2 != 0.3 in floating point! 0.1 has no exact binary representation. Always compare floats with a tolerance, never with ==.
float_gotcha.cpp
#include <iostream>
#include <iomanip>

int main() {
    double a = 0.1 + 0.2;
    std::cout << std::setprecision(20) << a << "\n";
    std::cout << (a == 0.3 ? "equal" : "NOT equal") << "\n";
}

6. Characters & ASCII

A char is one byte. The character 'A' is just the integer 65. 'a' is 97. '0' is 48. ASCII assigns codes 0–127. UTF-8 extends beyond 127 for international text.

ascii.cpp
#include <iostream>

int main() {
    for (char c = 'A'; c <= 'F'; ++c) {
        std::cout << c << " = " << static_cast<int>(c) << "\n";
    }
}

7. Practice Problems

  1. Convert 156 to 8-bit binary by hand.
    ✨ Show Answer

    156 = 128 + 16 + 8 + 4 = 1001 1100.

  2. What is the 8-bit two's complement representation of -1?
    ✨ Show Answer

    +1 = 0000 0001. Invert = 1111 1110. Add 1 = 1111 1111. So -1 = all ones (0xFF).

  3. Why does int8_t overflow at +127, not +255?
    ✨ Show Answer

    It's a signed 8-bit type. One bit is used for sign, leaving 7 bits for magnitude → range -128 to +127. uint8_t uses all 8 bits → 0 to 255.

  4. Print the size in bytes of char, short, int, long, long long, float, double.
    ✨ Show Answer
    sizes2.cpp
    #include <iostream>
    int main() {
        std::cout << sizeof(char) << " " << sizeof(short) << " "
                  << sizeof(int) << " " << sizeof(long) << " "
                  << sizeof(long long) << " " << sizeof(float) << " "
                  << sizeof(double) << "\n";
    }
  5. Why is comparing floats with == dangerous?
    ✨ Show Answer

    Most decimal fractions can't be represented exactly in binary. 0.1 + 0.2 equals 0.30000000000000004, not 0.3. Always use std::abs(a - b) < epsilon.

  6. Print the ASCII code of the character '?'.
    ✨ Show Answer
    ascii2.cpp
    #include <iostream>
    int main() {
        std::cout << static_cast<int>('?') << "\n"; // 63
    }
  7. What is endianness?
    ✨ Show Answer

    The order in which bytes of a multi-byte integer are stored. Little-endian (Intel/AMD): least significant byte first. Big-endian (some networks, PowerPC): most significant byte first. Matters when reading binary files or network data.

  8. Convert 0xCAFE to decimal.
    ✨ Show Answer

    C×16³ + A×16² + F×16 + E = 12·4096 + 10·256 + 15·16 + 14 = 49152 + 2560 + 240 + 14 = 51966.

  9. Why does int x = 2147483647 + 1; sometimes give -2147483648?
    ✨ Show Answer

    32-bit signed int max is 2,147,483,647 (0x7FFFFFFF). Adding 1 wraps to 0x80000000 = -2,147,483,648 in two's complement. Signed overflow is undefined behavior in C++.

  10. Write a loop that prints all powers of 2 up to 210.
    ✨ Show Answer
    pow2.cpp
    #include <iostream>
    int main() {
        int p = 1;
        for (int i = 0; i <= 10; ++i) {
            std::cout << "2^" << i << " = " << p << "\n";
            p *= 2;
        }
    }
  11. Difference between float and double?
    ✨ Show Answer

    float = 32 bits, ~7 decimal digits precision. double = 64 bits, ~15 decimal digits. Use double by default; only use float when memory is tight (huge arrays, GPU code).

  12. Convert '9' - '0'. What's the value and why?
    ✨ Show Answer

    '9' = ASCII 57, '0' = ASCII 48. 57 - 48 = 9. This is the standard idiom to convert a digit character to its numeric value.

Summary

Everything in memory is bits — meaning comes from interpretation. Use <cstdint> for fixed-width integers in portable code. Two's complement makes signed arithmetic just work with the same hardware adder. IEEE 754 floats are imprecise — never compare them with ==. ASCII codes for digits and letters are contiguous, enabling clean idioms.

Next Module → Variables, Types & auto.