Why JavaScript Float Addition Is Inaccurate: Inside V8’s Machine Code
This article explains how the V8 JavaScript engine generates machine code for floating‑point addition on Intel x64, covering V8’s architecture, the IEEE‑754 representation that causes precision loss, and a step‑by‑step analysis of the relevant C++ source and the resulting addsd instruction.
Abstract
This article introduces how the V8 engine generates machine code for floating‑point addition on the Intel x64 platform. It first gives a brief overview of V8, then explains why floating‑point addition results are inaccurate from C and x64 assembly perspectives, and finally examines the V8 source code that produces the corresponding machine code.
V8 Overview
V8 is Google’s open‑source high‑performance JavaScript and WebAssembly engine written in C++. It powers Chrome, Node.js, and many other applications, running on Windows, macOS, and Linux across x64, IA‑32, ARM, and MIPS architectures. The source tree contains many directories; the core JavaScript‑related code resides in the src folder.
The compilation pipeline is: the parser converts JavaScript to an AST (in src/parsing and src/ast), the interpreter generates bytecode ( src/interpreter), and the optimizing compiler later translates hot bytecode into machine code ( src/compiler and src/codegen).
Why JavaScript Float Operations Are Inaccurate
Floating‑point numbers follow the IEEE‑754 double‑precision format (64 bits: 1 sign bit, 11 exponent bits, 52 mantissa bits). Because the mantissa is limited, adding numbers with vastly different exponents can lose low‑order bits, causing results like a == d to be true even though mathematically a < d.
#include <stdio.h>
#include <math.h>
int main() {
double a = pow(2, 100);
double b = pow(2, 47);
double c = pow(2, 48);
double d = a + b;
double e = a + c;
printf("a == d is %d
", a == d);
printf("a == e is %d
", a == e);
}On x86, the exponent alignment shifts the smaller operand, and the extra mantissa bit is discarded, leading to the observed equality.
How V8 Generates Float‑Addition Machine Code
On x64, the floating‑point addition instruction is addsd (or vaddsd). V8 emits this instruction via the Assembler::addsd method, which writes the opcode bytes 0xF2 0x0F 0x58 and then calls emit_sse_operand to produce the ModR/M byte 0xC8 for addsd xmm1, xmm0.
void Assembler::addsd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
emit(0xF2);
emit_optional_rex_32(dst, src);
emit(0x0F);
emit(0x58);
emit_sse_operand(dst, src);
}The emit_sse_operand method constructs the final byte by combining the high bits 0xC0 with the low‑order bits of the destination and source registers:
void Assembler::emit_sse_operand(XMMRegister dst, XMMRegister src) {
emit(0xC0 | (dst.low_bits() << 3) | src.low_bits());
}For dst = xmm1 (code 1) and src = xmm0 (code 0), the calculation yields 0xC0 | 0x08 | 0x00 = 0xC8, completing the four‑byte machine code F2 0F 58 C8 for the addition.
Conclusion
The V8 engine translates JavaScript floating‑point addition into the addsd instruction by emitting the exact opcode bytes defined by the Intel manual. Understanding this process clarifies why floating‑point precision issues arise and how V8’s code generator implements the operation at the machine‑code level.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Weidian Tech Team
The Weidian Technology Platform is an open hub for consolidating technical knowledge. Guided by a spirit of sharing, we publish diverse tech insights and experiences to grow and look ahead together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
