Conversion, Coercion & Parsing at the Boundary

Widening and narrowing conversions, implicit coercion and the comparison tables it produces, why casting and parsing are different operations, and the failure modes — silent truncation, locale-dependent parsing, and type-juggling authentication bypasses.

Three different operations get called "conversion" and behave nothing alike: widening (always safe, information-preserving), narrowing (may destroy information, so somebody must decide what happens when it does), and parsing (turning text into a value, which can simply fail). Languages differ mostly in how loudly they make you acknowledge the second and third. The bugs cluster exactly where the language stays quiet.

Widening, narrowing, and who checks #

Widening conversions — int → long, float → double, subclass → superclass — are implicit everywhere because no value is lost. Narrowing is where the design choice lives.

java
long  big = 3_000_000_000L;
int   n   = (int) big;   // -1294967296 — silently wraps, no exception
double d  = 1.9;
int   m   = (int) d;     // 1 — truncates toward zero, not rounds

Java, C, C++, C# (unchecked) and Go all truncate silently on integer narrowing. The alternatives exist and are worth knowing:

Language Narrowing integer conversion
Java, Go, C silent wraparound / truncation
C# silent by default; checked { } block throws OverflowException
Rust as truncates silently; TryFrom/try_into() returns Result, and the idiomatic code uses that
Python no fixed-width ints — arbitrary precision, so the problem does not arise
Swift traps on overflow by default; &+ opts into wrapping

Implicit coercion #

Coercion is conversion the language performs on your behalf to make an operation type-check. It is convenient in the small and produces tables like this in the large:

javascript
[] + {}        // "[object Object]"   — both coerced to string
[] == false    // true   — [] → "" → 0, false → 0
"0" == false   // true
"" == 0        // true
null == 0      // false  — null only loosely equals undefined
NaN == NaN     // false
[1,10,2].sort() // [1,10,2] → compares "1" < "10" < "2" as strings

None of this is random — it is ToPrimitive followed by the abstract equality algorithm in ECMA-262 — but no one holds it in their head, which is why the practical rule in JavaScript and TypeScript is: use === always, and use an explicit Number()/String() when you want a conversion.

Python 3 took the opposite path deliberately: 1 + "1" is a TypeError, and that break with Python 2 was one of the migration's biggest costs and one of its clearest wins.

Casting is not parsing #

A cast re-describes a value the program already has, at compile time or with a runtime identity check. Parsing builds a value from external text and can legitimately fail.

csharp
object o = "42";
int a = (int) o;                       // InvalidCastException — a string is not a boxed int
int b = int.Parse("42");               // parse — throws on bad input
bool ok = int.TryParse(s, out int c);  // parse — returns failure instead of throwing

The distinction matters because they need different error handling. A failed cast is a programming error — the code believed something false about its own data. A failed parse is an expected outcome of untrusted input, and code that treats it as exceptional will either crash on ordinary bad input or swallow it. See error handling strategies.

Floating point deserves its own caution #

double holds integers exactly only up to 2532^{53}. Beyond that, converting a 64-bit integer to a double loses low bits silently — which is why JSON, whose number type is a double in most parsers, cannot round-trip a 64-bit ID. Twitter hit this in 2010 and the standard fix has been the same ever since: serialize 64-bit identifiers as strings.

json
{ "id": 10765432100123456789, "id_str": "10765432100123456789" }

0.1+0.2≠0.30.1 + 0.2 \neq 0.3 for the same reason — neither operand is representable in binary. Money is never a float; it is an integer count of minor units or a decimal type (floating-point hardware).

Sources

  1. docsESA — Ariane 501 Inquiry Board report (Lions et al., 19 July 1996)
  2. docsESA Science — Ariane 501, Report by Board of Inquiry
  3. docsECMA-262 — Abstract Equality Comparison and ToPrimitive
  4. docsPHP Manual — Comparison operators and type juggling (PHP 8 string/number comparison change)
  5. docsThe Java Language Specification — Conversions and Contexts

Searches titles, aliases, headings, summaries, and full text.↑↓ move ↵ open esc close