Number Rendering¶
The normative rule is in the Bars specification; this page covers how it appears in fixtures and in the reference implementation.
{{ n }} renders a number exactly as ECMA-262 Number.prototype.toString() (radix 10) would: the
shortest decimal string that round-trips to the same IEEE-754 double, with ECMA's fixed/exponential
notation selection. Every kBars target, and every port to another language, must produce
byte-identical output for the same numeric value, rather than delegating to a host language's native
Double.toString() (which, on the JVM and Kotlin/Native, is not always shortest-round-trip).
1.0 -> "1" 2.0 -> "2" 100.0 -> "100"
1e20 -> "100000000000000000000" 1e21 -> "1e+21"
1e-6 -> "0.000001" 1e-7 -> "1e-7"
-0.0 -> "0" 0.1+0.2 -> "0.30000000000000004"
Double.MAX_VALUE -> "1.7976931348623157e+308" Double.MIN_VALUE -> "5e-324"
NaN -> "NaN" Infinity -> "Infinity" -Infinity -> "-Infinity"
The reference implementation (:kbars commonMain, impl/number/Ryu.kt and
EcmaNumberFormat.kt) generates shortest round-trip digits by porting the digit-generation core of
ulfjack/ryu (Apache-2.0-or-Boost) and layers a hand-written
ECMA-262 notation selector on top. A port does not need to reuse Ryu specifically — any correct
shortest-round-trip digit generator (Grisu3, Dragon4, a bundled bignum library, or your host
language's own Number.prototype.toString() if it happens to be JavaScript) is acceptable, provided
the output text matches these fixtures.
Notation selection¶
Given the shortest round-trip digit string and its decimal exponent, ECMA-262 chooses between fixed and exponential notation using fixed thresholds (not "however many digits look reasonable"):
- Magnitudes from
1e-6(inclusive) up to1e21(exclusive) use fixed notation. - Everything else — very large or very small magnitudes — uses exponential notation, formatted
as
<digit>[.<digits>]e<+|->EXP.
-0.0 renders "0" (the sign is dropped); NaN, Infinity, and -Infinity render as those exact
literal words, not numeric notation.
Integer vs. float is not observable in rendered output¶
2 and 2.0 both render "2" — kBars does not echo the source literal's int/float distinction. The
distinction survives only in comparison (==, <, etc.), which already coerces both operands to
Double before comparing, so equality and truthiness treat 2 and 2.0 identically as well. A port
that instead echoes the source JSON literal (preserving "2.0" because the source said 2.0), or
that delegates digit generation to its host language's native number-to-string conversion without
checking it against shortest-round-trip, will diverge from these fixtures.
Fixtures¶
conformance/render/number-rendering.jsonlocks the ECMA-262 notation-boundary cases shown above (whole doubles, over-Longintegers, the1e20/1e21and1e-6/1e-7fixed/exponential thresholds,-0.0,0.1 + 0.2,Double.MAX_VALUE/Double.MIN_VALUE).conformance/render/int-float-parity.jsonlocks that2and2.0render identically and compare/truthy identically.
See also the consumer-facing summary at Number Rendering in the kBars usage guide.