DEV Community

Cover image for Where did the 0.5 go? An Initial Exploration of C Type Coercion and CPU Registers
Eric Miguel
Eric Miguel

Posted on

Where did the 0.5 go? An Initial Exploration of C Type Coercion and CPU Registers

During a classroom discussion with some classmates, we ended up on a curious topic: what actually happens when you add values of different types in C? As someone who works primarily with high-level languages, I never truly took the time to explore C's guts, but the question stuck with me. A simple snippet summed it up:

// what is the value of x?
int x = 5;
x += 2.5;
Enter fullscreen mode Exit fullscreen mode

A printf of x shows 7 in the terminal. The calculation was done, so at some point, 7.5 had to exist. I'm assuming here the computer respects basic math. But where did it live?

Type Coercion and the Math Behind It

Addition seems trivial, but in C, when different types operate together, usual arithmetic conversions kick in.

Mathematically, what’s happening is a set promotion. If we have x∈Z (an integer) and add y∈R (a floating-point value), C promotes x to the Real set to make the operation possible.

However, since the final destination is an int, the result undergoes a floor function (or truncation):

⌊5.0 + 2.5⌋ = 7
Enter fullscreen mode Exit fullscreen mode

The value 7.5 is temporarily treated as a double, and the result is truncated, not rounded.

Who handles the conversion? Where does the 7.5 live?

The compiler generates Assembly instructions that operate on registers: tiny, ultra-fast memory slots inside the CPU. By inspecting the generated code with the -S flag in GCC (gcc -S main.c), we can see exactly what happens:

cvtsi2sd    -4(%rbp), %xmm0    # Convert int to double (xmm0 = 5.0)
addsd       .LC0(%rip), %xmm0  # Add the value 2.5
cvttsd2si   %xmm0, %eax        # Truncate (cvtt) back to int
Enter fullscreen mode Exit fullscreen mode

The value 7.5 exists briefly in the xmm0 register. The cvttsd2si instruction (the "tt" stands for Truncate) is responsible for discarding the decimal part before moving the value back to RAM.

Reproducing it with Inline Assembly

To see this process step-by-step and "materialize" the ephemeral state of the register, I used __asm__ volatile to capture the value of xmm0 right before truncation:

#include <stdio.h>

static void dump(const char *label, const void *ptr, size_t size)
{
    const unsigned char *bytes = ptr;
    printf("  %s: ", label);
    for (size_t i = 0; i < size; i++)
        printf("%02x ", bytes[i]);
    printf("\n");
}

int main(void)
{
    int x = 5;
    double reg_before;       // register value before truncation
    int    result;           // value after truncation
    double two_and_a_half = 2.5;

    __asm__ volatile (
        "cvtsi2sd  %[xi],    %%xmm0       \n\t"  // xmm0 = (double)5.0
        "addsd     %[half],  %%xmm0       \n\t"  // xmm0 = 7.5
        "movsd     %%xmm0,   %[capture]   \n\t"  // CAPTURE the actual 7.5
        "cvttsd2si %%xmm0,   %[res]       \n\t"  // truncate to 7
        : [capture] "=m" (reg_before),
          [res]     "=r" (result)
        : [xi]      "r"  (x),
          [half]    "m"  (two_and_a_half)
        : "xmm0"
    );

    dump("Value in xmm0 (pre-truncation)", &reg_before, sizeof(reg_before));
    dump("Truncated result (int)",         &result,     sizeof(result));

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Terminal output:

Value in xmm0 (pre-truncation): 00 00 00 00 00 00 1e 40  <- 7.5 (IEEE 754)
Truncated result (int):         07 00 00 00              <- 7   (Integer)
Enter fullscreen mode Exit fullscreen mode

The dump function reveals the raw representation: x temporarily occupied 8 bytes in IEEE 754 floating-point format to hold the necessary precision, before being "crushed" back into the 4 bytes of an integer.

Final Thoughts

C is often called a low-level language, yet this small experiment shows it possesses abstraction layers that silently decide how mathematics is interpreted by silicon.

For those of us on the path of Applied Mathematics, realizing that a "simple" + sign can involve type promotions and operations in 128-bit registers is a reminder that in computing, numbers are rarely as simple as they look on paper.

Top comments (0)