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