console: %d prints Number(value), as node does
RO
21d ago+77+77+01234567890123456789-73-73-01234567890123456789
Stacked on #45935. This PR holds one decision: what %d prints.
Problem
console.log('%d', 3.9)prints3. Node prints3.9. In Bun,util.format('%d', 3.9),new console.Console()and the console of anode:worker_threadsWorker print3.9too.%dand%ihave shared one integer conversion since 2022 (d3a93d5273). The WHATWG console formatter hasparseInt(value, 10)for both. Node hasNumber(value)for%d.
Change
%dprintsNumber(value): with its fraction, its exponent form and the sign of its zero.%ikeeps the integer.- This changes behaviour. It is not a fix. A maintainer must choose node's text or the integer. To keep the integer, close this PR: #45935 does not depend on it.
- Verified:
test/js/web/console/console-log.test.ts(the base branch fails 5 tests). 0 of 336 table cells differ from node v26.3.0.
Background
- The global console is the object that
node:consoleexports. Its native format scanner is the only%din Bun that truncates. - #45935 makes
%iand%fconvert as node does, prints BigInt arguments and makes the digits of%dexact. It leaves%dan integer.
Downsides
%doutput changes on 38 of 112 table values:3.9,1e21,-0,Infinity,'3.14'. A program or a snapshot that needs the integer must use%i.%dof1234.5costs 316 more instructions than on the base branch (2,263 to 2,579 for the call). It prints through the double formatter.- After
%dof a Number with a fraction, a trailing array can break onto new lines earlier: 5 of 915 swept calls.
Notes
The 38 values, base branch to this PR (this PR equals node on each):
| value | base | this PR |
|---|---|---|
3.9, -3.9, 0.5, 0.1 + 0.2 | 3, -3, 0, 0 | 3.9, -3.9, 0.5, 0.30000000000000004 |
-0.5, -0.9999999, -0 | 0 | -0.5, -0.9999999, -0 |
1e21, -1e21, 1.5e300, Number.MAX_VALUE | 1, -1, 1, 1 | 1e+21, -1e+21, 1.5e+300, 1.7976931348623157e+308 |
0.000001, 0.00000099, 5e-7, -8.5e-12 | 0, 9, 5, -8 | 0.000001, 9.9e-7, 5e-7, -8.5e-12 |
Number.MIN_VALUE, Number.MIN_VALUE * 1.5 | 5, 1 | 5e-324, 1e-323 |
2147483648.7, -2147483649.7 | 2147483648, -2147483649 | 2147483648.7, -2147483649.7 |
999999999999998.25, -1000000000000000.25 | 999999999999998, -1000000000000000 | 999999999999998.2, -1000000000000000.2 |
Infinity, -Infinity, 'Infinity', '-Infinity', '+Infinity', '1e400', '-1e400' | NaN | Infinity or -Infinity |
'-0', '-0.0', '-.5', '.5' | 0 | -0, -0, -0.5, 0.5 |
'\u00a042.5', '\ufeff42.5' | 42 | 42.5 |
[3.9], { toString: () => '8.5' }, new Number(4.5), new String('6.5') | 3, 8, 4, 6 | 3.9, 8.5, 4.5, 6.5 |
For node's %d.
- Node documents
console.logasutil.formatof its arguments, and prints3.9. - Three of the four
%dpaths in Bun print the Number today:util.format,new console.Console(), and the console of anode:worker_threadsWorker. #39592 moves that Worker console onto the native scanner. - 5,000 generated strings and 17,644 generated Numbers (fixed seeds): 0 lines differ from
util.formatunder%d,%iand%fon this branch.
For the integer.
%dhas printed an integer since d3a93d5273 (January 2022). #8733 wrote these arms from the WHATWG formatter, which hasCall(%parseInt%, undefined, « current, 10 »)for%dand%i.console-log.expected.txtlines 206, 207, 219 and 220 hold the integer. This PR changes them to node's values.Bun.WebViewwithconsole: globalThis.consolereplays a page's console calls through this scanner. With the WebKit backend a page's%dof3.9would print3.9where Safari prints3. That backend was not run.
Cost. Release builds. User instructions of one console.log call, main 017b2439a7, base branch, this PR:
| call | main | base | this PR |
|---|---|---|---|
%d of an int32 | 2,337 | 2,342 | 2,344 |
%d of 1700000000000 | 2,434 | 2,312 | 2,314 |
%d of 1234.5 | 2,393 | 2,263 | 2,579 |
%d of '42' | 2,643 | 2,585 | 2,589 |
%d of undefined | 2,211 | 2,293 | 2,278 |
%i of 1234.5 | 2,393 | 2,261 | 2,278 |
%f of 1234.5 | 3,470 | 2,578 | 2,579 |
size: text 106,743,054 to 106,742,798 bytes (256 fewer than the base branch). The file size does not change.- Line layout: 5 of 915 swept calls change, all under
%dof1234.5with 63 to 67 columns of text before it. 6,000 more calls with one substitution and a trailing value: 0 change. - The line-width estimate counts the text of a number that does not print as only digits: a fraction, an exponent form,
-0,NaN,Infinity. A whole number that prints as digits adds one column, as on main.console-log.jshas a row for%dofNumber.MAX_VALUE. cargo clippyforbun_jsc,cargo fmt --checkpass.BUN_JSC_validateExceptionChecks=1on the test file: 10 pass, 1 todo, 0 reports.