f64T
On this page

f64T

Constant in Types

The emulated double-precision scalar type.

import { f64T } from 'typeshade'

The signature, the description and the examples come from the compiler's own source at commit 26de7be8.

Syntax

const f64T: { readonly kind: "f64"; }

Description

The emulated double-precision scalar type. It is a logical f64, since no GPU has one: a value typed f64T is an unevaluated pair of f32 values, a high part and a low part holding the residual the high part could not represent, and a pass that runs before emit rewrites every operation on it into arithmetic on that pair. The pair carries about 48 significand bits at f32’s exponent range, against f32’s 24, so it costs one f32 pair per value and no real 64-bit register.

The authoring surface is the same as f32’s; only the declared type differs. Declare a parameter, a uniform field or a struct field f64T and the operators, the comparisons and the builtins read exactly as they do for f32.

These operations are emulated: +, -, *, /, all comparisons, neg, abs, min, max, sqrt, mix with an f32 interpolant, floor, fract, sin and cos, and on the vector types vec2f64T and its siblings also dot, length, distance and normalize. Anything else on an f64 operand fails at emit with SD0041, naming the operation: narrow explicitly with toF32 first. % and the bitwise operators are rejected at author time, since neither has a meaning on a two-part value.

Conversion goes one way implicitly. An f32 widens to f64 in arithmetic, exactly, and toF64 or a bare number literal does it explicitly; a JavaScript number is already a double, so a literal splits losslessly at build time. Narrowing is always explicit, toF32, and loses precision. Mixing f64 with an integer or a boolean is SD0004 at author time.

An f64 varying is rejected with SD0044: interpolating a high and low pair independently is numerically wrong. Narrow to f32 for the varying, or carry the two parts as two f32 locations and rebuild them with f64FromParts.

sin and cos are less accurate than the arithmetic. They use a three-stage argument reduction, a tabled angle addition and a short Taylor series on the remainder, and the truncation floors the relative error at about 2^-36 for the transcendental itself, which then degrades with the argument’s magnitude through the reduction. That is still far past f32, whose sine of an argument near 2^24 is noise.

Each f64 operation costs several to ten times an f32 one, so opt in per value: declare f64 only where the precision is needed.

A module doing f64 arithmetic gets a guard texture injected automatically: a 1 by 1 texture_2d<f32> binding named _fp64 whose texel, always 1.0, multiplies the error-compensation terms so a downstream compiler cannot fold them away. fp64Guard pins its slot.

Examples

Example

import { fn, module, uniformStruct, sqrt, toF32, f64T, f32T } from 'typeshade'
const U = uniformStruct('U', { group: 0, binding: 0, as: 'u' }, { origin: f64T })
// The operators are unchanged; only the declared type says f64.
const k = fn('k', { x: f64T, s: f32T }, ({ x, s }) => toF32(sqrt(x.add(U.field.origin).mul(s))))
const m = module({ uses: [U], funcs: [k] })

In the guide

See also

Source

src/core/ir/types.ts, line 376, at commit 26de7be8

Edit this page Report a problem