Emulated double

Emulated double

Neither target has a 64-bit float. An f64 is a pair of f32 words, rewritten into vec2<f32> and df64_ calls before either backend sees it. fp64-lane-stripes draws the f32 and f64 (emulated) paths side by side.

  • f64 scalar

    A pair of f32 words, with a df64_ call for each operation. fp64-lane-stripes

    TypeScript
    export function widened(a: f64, b: f64): f64 {
    return a * b + a
    }
    Emitted WGSL
    fn widened(a: vec2<f32>, b: vec2<f32>) -> vec2<f32> {
    let _fp64_g = textureLoad(_fp64, vec2<i32>(0, 0), 0).x;
    return df64_add(df64_mul(a, b, _fp64_g), a, _fp64_g);
    }
  • f64 literal

    A literal in a declared f64 position keeps the whole double.

    TypeScript
    export function stripe(a: f64): f64 {
    const width: f64 = 0.125
    return a * width
    }
    Emitted WGSL
    fn stripe(a: vec2<f32>) -> vec2<f32> {
    let _fp64_g = textureLoad(_fp64, vec2<i32>(0, 0), 0).x;
    let width = vec2<f32>(0.125, 0.0);
    return df64_mul(a, width, _fp64_g);
    }
  • f64 vector

    A vector of doubles, lowered into a hi plane and a lo plane. vec2d is the short spelling of vec2f64.

    TypeScript
    export function narrowed(
    p: vec3f64, q: vec2d
    ): f32 {
    return f32(p.x) + f32(q.y)
    }
    Emitted WGSL
    fn narrowed(p: DF64Vec3, q: DF64Vec2) -> f32 {
    return (df64_narrow(vec2<f32>(p.hi.x, p.lo.x)) + df64_narrow(vec2<f32>(q.hi.y, q.lo.y)));
    }
  • Builtin on an f64

    Ten builtins have an emulated body on a scalar and thirteen on a vector.

    TypeScript
    export function rooted(a: f64): f64 {
    return sqrt(abs(a))
    }
    Emitted WGSL
    fn rooted(a: vec2<f32>) -> vec2<f32> {
    let _fp64_g = textureLoad(_fp64, vec2<i32>(0, 0), 0).x;
    return df64_sqrt(df64_abs(a), _fp64_g);
    }
  • Builtin with no f64 body

    Refused

    Refused at the call, which names the ten and the narrow to write.

    TypeScript
    export function rounded(a: f64): f64 {
    return ceil(a)
    }

    Compiler diagnostic

    TS8036 ceil has no emulated-double form; got f64. On an f64 the pass lowers abs, cos, floor, fract, max, min, mix, round, sin and sqrt — narrow first, e.g. ceil(f32(x)).

  • f64 in a uniform

    A _fp64 texture is injected, which the host fills with 1.0. Reflection lists it like any binding.

    TypeScript
    class Uniforms {
    origin: f64
    span: f32
    }
    declare const u: uniform<Uniforms>
    Emitted WGSL
    @group(0) @binding(1) var _fp64: texture_2d<f32>;
  • f64 across stages

    Refused

    Read the double in the stage that needs it, or narrow it to an f32 at the boundary.

    TypeScript
    @fragment
    export function fs(
    @location(0) depth: f64
    ): vec4 {
    return vec4(f32(depth))
    }

    Compiler diagnostic

    TS8038 Parameter "depth" carries f64: an emulated double is a pair of f32 words, and a @location varying interpolates each word on its own, which is not the interpolation of the double. Narrow it with f32(x), or compute the double in the stage that needs it — a uniform or storage binding carries an f64 and every stage can read one.

Edit this page Report a problem