From 6c0abe676e427ea29b54c0e47806ad1b7e60f7e2 Mon Sep 17 00:00:00 2001 From: midagedev Date: Thu, 15 Oct 2026 15:05:08 +0900 Subject: [PATCH 1/2] web: goFixed has one module The printer moves out of the run route so a page can use it without importing that route. --- web/src/gofixed.js | 16 ++++++++++++++++ web/src/run.js | 15 +-------------- web/test/gofixed.test.mjs | 2 +- 3 files changed, 18 insertions(+), 15 deletions(-) create mode 100644 web/src/gofixed.js diff --git a/web/src/gofixed.js b/web/src/gofixed.js new file mode 100644 index 0000000..40f752c --- /dev/null +++ b/web/src/gofixed.js @@ -0,0 +1,16 @@ +// goFixed is strconv.FormatFloat(v, 'f', digits, 64). toFixed already rounds +// the exact binary value; the one difference is an exact tie, which toFixed +// takes away from zero and Go takes to even. A tie is a multiple of +// 2^-(digits+1), and that test is exact (a power-of-two scale). +// +// It lives here so the page chrome and the run route can both print with it. +// page.js must not import run.js: run.js imports the page. +export function goFixed(v, digits) { + const m = 10 ** digits; + const x = v * m; + if (Number.isInteger(v * 2 ** (digits + 1)) && !Number.isInteger(x)) { + const f = Math.floor(x); + return ((f % 2 === 0 ? f : f + 1) / m).toFixed(digits); + } + return v.toFixed(digits); +} diff --git a/web/src/run.js b/web/src/run.js index 6f8708e..c9e5e6d 100644 --- a/web/src/run.js +++ b/web/src/run.js @@ -10,6 +10,7 @@ // a door — the id is the secret (§9.3). import { avatarPath } from "./author.js"; +import { goFixed } from "./gofixed.js"; import { fail, json } from "./http.js"; import { esc, fmt, head, layout } from "./page.js"; @@ -659,20 +660,6 @@ function formatProbeMs(v) { return `${goFixed(v, 0)} ms`; } -// goFixed is strconv.FormatFloat(v, 'f', digits, 64). toFixed already rounds -// the exact binary value; the one difference is an exact tie, which toFixed -// takes away from zero and Go takes to even. A tie is a multiple of -// 2^-(digits+1), and that test is exact (a power-of-two scale). -export function goFixed(v, digits) { - const m = 10 ** digits; - const x = v * m; - if (Number.isInteger(v * 2 ** (digits + 1)) && !Number.isInteger(x)) { - const f = Math.floor(x); - return ((f % 2 === 0 ? f : f + 1) / m).toFixed(digits); - } - return v.toFixed(digits); -} - // memoryRow is the card's MEMORY lines that the index carries, in one cell: // "GPU0 10.4/12.0 GiB · host RSS 11.6 GiB (file 11.1 / anon 0.2)". Each // gpu_mem entry is "GPUn used/total GiB", or "GPUn used GiB" when the diff --git a/web/test/gofixed.test.mjs b/web/test/gofixed.test.mjs index 1b8d8a5..dd669d6 100644 --- a/web/test/gofixed.test.mjs +++ b/web/test/gofixed.test.mjs @@ -6,7 +6,7 @@ import assert from "node:assert/strict"; import test from "node:test"; -import { goFixed } from "../src/run.js"; +import { goFixed } from "../src/gofixed.js"; test("an exact tie rounds to even, as Go does", () => { assert.equal(goFixed(0.25, 1), "0.2"); From adff6184e94737b9ae904880465c749c02ac8982 Mon Sep 17 00:00:00 2001 From: midagedev Date: Thu, 15 Oct 2026 15:05:51 +0900 Subject: [PATCH 2/2] web: page figures use goFixed fmt() is the printer the hub shares with the card's formatRate. It still called toFixed, which prints the other way on an exact half. goFixed is the tie rule formatRate already uses. --- web/src/page.js | 6 +++++- web/test/gofixed.test.mjs | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/web/src/page.js b/web/src/page.js index 987891a..7841e07 100644 --- a/web/src/page.js +++ b/web/src/page.js @@ -1,3 +1,5 @@ +import { goFixed } from "./gofixed.js"; + // The chrome every page shares. // // One stylesheet, inline, because the pages are small and a published run @@ -222,5 +224,7 @@ export function esc(s) { // `?`; never print a default you did not observe). export function fmt(n) { if (!n) return "?"; - return n >= 100 ? n.toFixed(0) : n.toFixed(1); + // The card's formatRate. goFixed is the tie rule; toFixed would print the + // other way on an exact half. + return n >= 100 ? goFixed(n, 0) : goFixed(n, 1); } diff --git a/web/test/gofixed.test.mjs b/web/test/gofixed.test.mjs index dd669d6..6f8bbc2 100644 --- a/web/test/gofixed.test.mjs +++ b/web/test/gofixed.test.mjs @@ -6,6 +6,7 @@ import assert from "node:assert/strict"; import test from "node:test"; +import { fmt } from "../src/page.js"; import { goFixed } from "../src/gofixed.js"; test("an exact tie rounds to even, as Go does", () => { @@ -16,6 +17,13 @@ test("an exact tie rounds to even, as Go does", () => { assert.equal(goFixed(-0.25, 1), "-0.2"); }); +test("fmt uses that tie rule for the figures the page shares with the card", () => { + assert.equal(fmt(0.25), "0.2"); + assert.equal(fmt(0.75), "0.8"); + assert.equal(fmt(150.5), "150"); + assert.equal(fmt(0), "?"); +}); + test("a value that only looks like a tie rounds by its exact value", () => { // 0.15 and 1.45 are stored just under the tie (0.1499…, 1.4499…): Go prints 0.1 and 1.4. assert.equal(goFixed(0.15, 1), "0.1");