From 4b1ce4f509c1d99095437a586d9ca527f3645ee5 Mon Sep 17 00:00:00 2001 From: oxura Date: Wed, 2 Sep 2026 14:41:21 +0600 Subject: [PATCH 1/2] fix: emit shortest equivalent f32 literals --- packages/typegpu/src/tgsl/wgslGenerator.ts | 21 ++++++++++++++++++- packages/typegpu/tests/resolve.test.ts | 2 +- packages/typegpu/tests/std/bitcast.test.ts | 2 +- .../tests/std/texture/textureGather.test.ts | 2 +- .../tests/tgsl/extensionEnabled.test.ts | 2 +- .../typegpu/tests/tgsl/wgslGenerator.test.ts | 12 +++++++++++ 6 files changed, 36 insertions(+), 5 deletions(-) diff --git a/packages/typegpu/src/tgsl/wgslGenerator.ts b/packages/typegpu/src/tgsl/wgslGenerator.ts index 33d64ec786..3fef78cef3 100644 --- a/packages/typegpu/src/tgsl/wgslGenerator.ts +++ b/packages/typegpu/src/tgsl/wgslGenerator.ts @@ -1201,7 +1201,7 @@ export class WgslGenerator implements ShaderGenerator { // Just picking the shorter one const base = exp.length < decimal.length ? exp : decimal; if (schema.type === 'f32') { - return snip(`${base}f`, schema, /* origin */ 'constant', false); + return snip(`${shortestF32(value)}f`, schema, /* origin */ 'constant', false); } if (schema.type === 'f16') { return snip(`${base}h`, schema, /* origin */ 'constant', false); @@ -1902,6 +1902,25 @@ function assertExhaustive(value: never): never { throw new Error(`'${safeStringify(value)}' was not handled by the WGSL generator.`); } +function shortestF32(value: number): string { + const target = Math.fround(value); + if (Object.is(target, -0)) { + return '-0'; + } + + for (let precision = 1; precision <= 9; precision++) { + const rounded = Number(target.toPrecision(precision)); + const decimal = rounded.toString(); + const exponential = rounded.toExponential(); + const candidate = exponential.length < decimal.length ? exponential : decimal; + if (Math.fround(rounded) === target) { + return candidate; + } + } + + return target.toString(); +} + function parseNumericString(str: string): number { // Hex literals if (/^0x[0-9a-f]+$/i.test(str)) { diff --git a/packages/typegpu/tests/resolve.test.ts b/packages/typegpu/tests/resolve.test.ts index da33f3d922..aa5e8e09f8 100644 --- a/packages/typegpu/tests/resolve.test.ts +++ b/packages/typegpu/tests/resolve.test.ts @@ -410,7 +410,7 @@ describe('tgpu resolve - nesting', () => { } expect(tgpu.resolve([foo])).toMatchInlineSnapshot(` - "const pi: f32 = 3.141592653589793f; + "const pi: f32 = 3.1415927f; fn getPi2() -> f32 { return (pi * 2f); diff --git a/packages/typegpu/tests/std/bitcast.test.ts b/packages/typegpu/tests/std/bitcast.test.ts index f38370bbec..1421634f84 100644 --- a/packages/typegpu/tests/std/bitcast.test.ts +++ b/packages/typegpu/tests/std/bitcast.test.ts @@ -162,7 +162,7 @@ describe('bitcast in shaders', () => { expect(tgpu.resolve([fnf32])).toMatchInlineSnapshot(` "fn fnf32() -> f32 { - return 1.7292023049768243e-42f; + return 1.729e-42f; }" `); expect(tgpu.resolve([fni32])).toMatchInlineSnapshot(` diff --git a/packages/typegpu/tests/std/texture/textureGather.test.ts b/packages/typegpu/tests/std/texture/textureGather.test.ts index 087a76a3d2..1a9ee92f6c 100644 --- a/packages/typegpu/tests/std/texture/textureGather.test.ts +++ b/packages/typegpu/tests/std/texture/textureGather.test.ts @@ -81,7 +81,7 @@ describe('textureGather', () => { fn testFn() { let uv2d = vec2f(0.5); let uv3d = vec3f(0.5, 0.5, 0); - const idx = 1.2000000476837158f; + const idx = 1.2f; const component = 0i; let gather2d = textureGather(component, tex2d, sampler_1, uv2d); let gather2d_u32 = textureGather(component, tex2d_u32, sampler_1, uv2d); diff --git a/packages/typegpu/tests/tgsl/extensionEnabled.test.ts b/packages/typegpu/tests/tgsl/extensionEnabled.test.ts index 8252871bdb..afde14f718 100644 --- a/packages/typegpu/tests/tgsl/extensionEnabled.test.ts +++ b/packages/typegpu/tests/tgsl/extensionEnabled.test.ts @@ -19,7 +19,7 @@ describe('extension based pruning', () => { "enable f16; fn someFn() -> f32 { - return 6.599609375f; + return 6.5996094f; }" `); diff --git a/packages/typegpu/tests/tgsl/wgslGenerator.test.ts b/packages/typegpu/tests/tgsl/wgslGenerator.test.ts index 883cb66b88..499fa1a264 100644 --- a/packages/typegpu/tests/tgsl/wgslGenerator.test.ts +++ b/packages/typegpu/tests/tgsl/wgslGenerator.test.ts @@ -68,6 +68,18 @@ describe('WgslGenerator', () => { ).toBe('abstractFloat'); }); + it('emits the shortest f32 literal that preserves its value', () => { + const main = tgpu.fn( + [], + d.f32, + )(() => { + 'use gpu'; + return d.f32(0.3); + }); + + expect(tgpu.resolve([main])).toContain('return 0.3f;'); + }); + it('generates correct resources for member access expressions', ({ root }) => { const TestStruct = d.struct({ a: d.u32, From d0612bf4b27727f9c9af72f440ad399055ae4e8d Mon Sep 17 00:00:00 2001 From: oxura Date: Wed, 2 Sep 2026 16:35:43 +0600 Subject: [PATCH 2/2] refactor: tighten f32 literal generation --- packages/typegpu/src/tgsl/wgslGenerator.ts | 10 ++++++---- packages/typegpu/tests/tgsl/wgslGenerator.test.ts | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/typegpu/src/tgsl/wgslGenerator.ts b/packages/typegpu/src/tgsl/wgslGenerator.ts index 3fef78cef3..157ebb6b6e 100644 --- a/packages/typegpu/src/tgsl/wgslGenerator.ts +++ b/packages/typegpu/src/tgsl/wgslGenerator.ts @@ -1193,6 +1193,9 @@ export class WgslGenerator implements ShaderGenerator { if (schema.type === 'i32') { return snip(`${value}i`, schema, /* origin */ 'constant', false); } + if (schema.type === 'f32') { + return snip(`${shortestF32(value)}f`, schema, /* origin */ 'constant', false); + } const exp = value.toExponential(); const decimal = @@ -1200,9 +1203,6 @@ export class WgslGenerator implements ShaderGenerator { // Just picking the shorter one const base = exp.length < decimal.length ? exp : decimal; - if (schema.type === 'f32') { - return snip(`${shortestF32(value)}f`, schema, /* origin */ 'constant', false); - } if (schema.type === 'f16') { return snip(`${base}h`, schema, /* origin */ 'constant', false); } @@ -1902,13 +1902,15 @@ function assertExhaustive(value: never): never { throw new Error(`'${safeStringify(value)}' was not handled by the WGSL generator.`); } +const F32_MAX_SIGNIFICANT_DIGITS = 9; + function shortestF32(value: number): string { const target = Math.fround(value); if (Object.is(target, -0)) { return '-0'; } - for (let precision = 1; precision <= 9; precision++) { + for (let precision = 1; precision <= F32_MAX_SIGNIFICANT_DIGITS; precision++) { const rounded = Number(target.toPrecision(precision)); const decimal = rounded.toString(); const exponential = rounded.toExponential(); diff --git a/packages/typegpu/tests/tgsl/wgslGenerator.test.ts b/packages/typegpu/tests/tgsl/wgslGenerator.test.ts index 499fa1a264..e0ff05650a 100644 --- a/packages/typegpu/tests/tgsl/wgslGenerator.test.ts +++ b/packages/typegpu/tests/tgsl/wgslGenerator.test.ts @@ -77,7 +77,7 @@ describe('WgslGenerator', () => { return d.f32(0.3); }); - expect(tgpu.resolve([main])).toContain('return 0.3f;'); + expect(tgpu.resolve([main])).toMatch(/^\s*return 0\.3f;$/m); }); it('generates correct resources for member access expressions', ({ root }) => {