Core answer: 0.1 is an infinite repeating fraction in binary (0.0001100110011…), and a double's 52-bit mantissa must truncate it — a ~5.5×10⁻¹⁷ representation error, so 0.1+0.2 prints 0.30000000000000004. The fixes: compare with an epsilon, store money as integer cents, format only for display.

Why 0.1+0.2 is not 0.3

Decimal 0.1 is binary 0.00011001100110011... (0011 forever). With only 52 mantissa bits the value must round: 0.1 stores as ≈0.10000000000000000555, 0.2 as ≈0.20000000000000001110, and their rounded sum is 0.30000000000000004440… — printed as 0.30000000000000004. Not a JS bug — every IEEE754 language agrees.

IEEE754 double structure

PartBitsRole
Sign S10 positive, 1 negative
Exponent E11Real exponent + 1023 bias
Mantissa M52Implied leading 1; ~15-17 decimal digits

Value = (−1)^S × 1.M × 2^(E−1023). Specials: exponent all-zero with nonzero mantissa = subnormal; all-one exponent with zero mantissa = ±∞; all-one with nonzero mantissa = NaN. The [IEEE754 analyzer](/c/dev/ieee754) dissects any decimal into its 64 bits.

Safe comparison and money math

Comparison (never ===): Math.abs(a - b) < 1e-10

Money: store integer cents ($19.99 → 1999), compute in integers, format with (cents/100).toFixed(2) only at the edge. Safe integer ceiling: 2^53 = 9007199254740992 — beyond it use BigInt.

Bitwise operator table

OpNameRuleTypical use
&AND1 only if both 1Masking, odd/even
\OR1 if either 1Setting flags
^XOR1 if differentToggling, swaps
~NOTflips all bitsWith two's complement
<<Left shiftzeros in low bits×2ⁿ
>>Signed right shiftcopies sign bit÷2 floor
>>>Unsigned right shiftzeros in high bitsForce unsigned

JS bitwise ops first truncate operands to 32-bit signed integers — values above 2^31 overflow first.

Example: permission bits with masks

Read/write/execute in one integer: ① define read=4 (100), write=2 (010), exec=1 (001); ② grant read+write = 4|2 = 6; ③ check: 6&4=4 → has read; 6&1=0 → no exec; ④ add exec: 6|1=7; revoke write: 7&~2=5. This is exactly chmod 755 (rwxr-xr-x = 111101101). The [bitwise calculator](/c/dev/bitwise) shows the binary longhand for every step.

Example: dissecting a float64 by hand

0.1 as hex 0x3FB999999999999A: sign S=0; exponent 01111111011 = 1019 → real exponent −4; mantissa 10011001...1010 with the implied 1 → 1.6×2⁻⁴ ≈ 0.10000000000000000555. Seeing the 0011 loop physically truncated at bit 52 is where the error becomes real.

Common mistakes

  • Using toFixed for math: it formats display strings only — feeding it back into arithmetic re-enters float approximation. Keep the whole chain in integer cents.
  • Treating >> as unsigned: negative >> copies the sign bit (-4>>1=-2); for logical shift use >>> (-4>>>1=2147483646).
  • Comparing NaN to itself: NaN === NaN is false by spec — use Number.isNaN().