diff --git a/packages/typegpu/src/tgsl/wgslGenerator.ts b/packages/typegpu/src/tgsl/wgslGenerator.ts index 33d64ec786..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(`${base}f`, schema, /* origin */ 'constant', false); - } if (schema.type === 'f16') { return snip(`${base}h`, schema, /* origin */ 'constant', false); } @@ -1902,6 +1902,27 @@ 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 <= F32_MAX_SIGNIFICANT_DIGITS; 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..e0ff05650a 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])).toMatch(/^\s*return 0\.3f;$/m); + }); + it('generates correct resources for member access expressions', ({ root }) => { const TestStruct = d.struct({ a: d.u32,