---
title: "16-bit and 64-bit types: half, int16_t, double and uint64_t"
url: https://recursiveai.org/oneshader/cookbook/hlsl-16-bit-64-bit-types/
description: "Use native 16-bit half and int16_t and 64-bit double and uint64_t in HLSL with OneShader 1.2.0, get the matching C# structs, and see which device features…"
version: "1.2.0"
---

# 16-bit and 64-bit types: half, int16_t, double and uint64_t

Use native 16-bit `half` and `int16_t` and 64-bit `double` and `uint64_t` in HLSL with OneShader 1.2.0, get the matching C# structs, and see which device features each target needs and what Metal and WebGPU refuse.

OneShader 1.2.0 compiles the sized types the way DXC does. With 16-bit types enabled (DXC’s `-enable-16bit-types`), `half`, `min16float`, `min16int`, `min16uint` and the `float16_t`, `int16_t` and `uint16_t` families are native 16-bit types, in arithmetic and in buffers with DXC’s byte layout. `double`, `int64_t` and `uint64_t` work on SPIR-V in arithmetic, in buffers and in 64-bit `Interlocked*` operations.

## The project

16-bit types are switched on for the project. `Totals.hlsl` uses 64-bit types, which only SPIR-V has, so that file is built for SPIR-V only:

```xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <OneShaderEnable16BitTypes>true</OneShaderEnable16BitTypes>
    <OneShaderTargets>SpirV;Msl;Wgsl</OneShaderTargets>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="RecursiveAI.OneShader.Build" Version="1.2.0" />
    <!-- double and 64-bit integers exist only on SPIR-V. -->
    <OneShader Include="Shaders/Totals.hlsl" Targets="SpirV" />
  </ItemGroup>

</Project>
```

`Shaders/Particles.hlsl` keeps particle state in half-precision floats:

```hlsl
struct Particle
{
    half3 Velocity;      // 16-bit floats: half the bytes of a float3
    half Life;
    uint Flags;
};

cbuffer Step : register(b0) { half DeltaTime; uint Count; };
RWStructuredBuffer<Particle> Particles : register(u1);

[shader("compute")]
[numthreads(64, 1, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
    if (id.x >= Count)
        return;
    Particle p = Particles[id.x];
    p.Life -= DeltaTime;
    p.Velocity *= (half)0.98;
    if (p.Life <= 0)
        p.Flags |= 1;
    Particles[id.x] = p;
}
```

`Shaders/Totals.hlsl` keeps a 64-bit byte counter and a double-precision sum:

```hlsl
struct Sums
{
    uint64_t Bytes;      // a 64-bit counter that does not wrap at 4 GB
    double Energy;       // 64-bit float accumulation
};

StructuredBuffer<uint> Sizes : register(t0);
StructuredBuffer<float> Samples : register(t1);
RWStructuredBuffer<Sums> Result : register(u2);

[shader("compute")]
[numthreads(64, 1, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
    InterlockedAdd(Result[0].Bytes, (uint64_t)Sizes[id.x]);
    double e = (double)Samples[id.x] * (double)Samples[id.x];
    if (id.x == 0)
        Result[0].Energy += e;
}
```

`Program.cs`:

```csharp
using System.Runtime.InteropServices;
using OneShader.Layouts.WebGpu;
using Sized.Shaders;

// 16-bit members are System.Half, one field per component.
var p = new Particles.Particle { Velocity_X = (Half)1.5f, Velocity_Y = (Half)0, Velocity_Z = (Half)(-2), Life = (Half)3 };
Console.WriteLine($"Particle: {Particles.Particle.Size} bytes, Life at {Marshal.OffsetOf<Particles.Particle>("Life")}, Flags at {Marshal.OffsetOf<Particles.Particle>("Flags")}");
Console.WriteLine($"Step: {Particles.Step.Size} bytes, Count at {Marshal.OffsetOf<Particles.Step>("Count")}");

// 64-bit members are ulong and double.
Console.WriteLine($"Sums: {Totals.Sums.Size} bytes, Energy at {Marshal.OffsetOf<Totals.Sums>("Energy")}");

// What the device must enable.
Console.WriteLine($"Particles.CSMain.RequiredFeatures: {Particles.CSMain.RequiredFeatures}");
Console.WriteLine($"Totals.CSMain.RequiredFeatures: {Totals.CSMain.RequiredFeatures}");
if (Particles.Pipeline.TryToWebGpu(out WebGpuPipelineLayout? webgpu, out var error))
    Console.WriteLine($"WebGPU requiredFeatures: [{string.Join(", ", webgpu!.RequiredFeatures ?? [])}]");
else
    Console.WriteLine(error);

// half in WGSL is f16.
string wgsl = System.Text.Encoding.UTF8.GetString(Particles.CSMain.Wgsl);
Console.WriteLine(wgsl.Split('\n')[0]);
```

```text
Particle: 16 bytes, Life at 6, Flags at 8
Step: 16 bytes, Count at 4
Sums: 16 bytes, Energy at 8
Particles.CSMain.RequiredFeatures: Float16, StorageBuffer16, UniformBuffer16
Totals.CSMain.RequiredFeatures: Float64, Int64, Int64Atomics
WebGPU requiredFeatures: [shader-f16]
enable f16;
```

The layouts are DXC’s. `dxc -enable-16bit-types -spirv` on the same `Particles.hlsl` gives member offsets 0, 6 and 8 and an array stride of 16, and `dxc -spirv` on `Totals.hlsl` gives offsets 0 and 8 and a stride of 16, as OneShader does. Both SPIR-V files pass `spirv-val`, and the WGSL passes naga.

## What the generated C# looks like

- `half` is `System.Half`, `int16_t` `short`, `uint16_t` `ushort`, `int64_t` `long`, `uint64_t` `ulong`, `double` `double`.
- Vectors of these have no `System.Numerics` type, so they’re one field per component: `half3 Velocity` becomes `Velocity_X`, `Velocity_Y` and `Velocity_Z`. A matrix of a 16-bit or 64-bit element is an error that says to split it.
- **`RequiredFeatures`** is on every entry-point class and its `Layout`. Enable those device features when creating the Vulkan device (`PipelineLayoutInfo.RequiredFeatures` lists them too), and request `shader-f16` on WebGPU.
- Turn 16-bit types on per project with `<OneShaderEnable16BitTypes>`, per file with `Enable16BitTypes="true"` metadata, with `-enable-16bit-types` on the command line, or with `CompileOptions.Enable16BitTypes` at run time.

## Without 16-bit types

As in DXC, `half` and the `min16*` types are then 32-bit values (marked `RelaxedPrecision` on SPIR-V), and the explicitly sized names are an error:

```text
i16.hlsl(1,20): error OSH0360: The type 'uint16_t' needs 16-bit types enabled. Compile with -enable-16bit-types (MSBuild: OneShaderEnable16BitTypes=true), or use half, min16float, int or uint.
```

A `double` passed to a float-only intrinsic is narrowed to `float`, as DXC does, with a warning:

```text
dn.hlsl(2,64): warning OSH0361: 'sqrt' has no double version: the argument is converted to float, so the result has float precision. Use +, -, *, /, min, max, abs, clamp or fma to keep double precision.
```

## What each target has

| Type | SPIR-V | Metal | WGSL |
|---|---|---|---|
| `half` | yes | yes (native `half`) | yes (`f16`, `enable f16;`) |
| `int16_t`, `uint16_t` | yes | yes (`short`, `ushort`) | no |
| `int64_t`, `uint64_t` | yes | yes (`long`, `ulong`), not as vectors in buffers | no |
| `double` | yes | no | no |
| 64-bit `Interlocked*` | yes, on structured buffers and `groupshared` | only unsigned `InterlockedMax`/`Min` on a buffer, result unused | no |

A refusal is an error at the first use, only when that target is selected, with what to change. `Totals.hlsl` compiled for Metal and WGSL:

```text
/tmp/oneshader-docs/cookbook/Sized/Shaders/Totals.hlsl(15,5): error OSH5102: MSL has no 64-bit floating-point type. Use float, or compile this shader for SPIR-V only.
/tmp/oneshader-docs/cookbook/Sized/Shaders/Totals.hlsl(15,5): error OSH5102: Metal 2.4 supports 64-bit atomics only as unsigned InterlockedMax / InterlockedMin on a buffer without using the returned value. Use that form, 32-bit atomics, or compile this shader for SPIR-V only.
/tmp/oneshader-docs/cookbook/Sized/Shaders/Totals.hlsl(15,5): error OSH5202: WGSL has no 64-bit floating-point type. Use float, or compile this shader for SPIR-V only.
/tmp/oneshader-docs/cookbook/Sized/Shaders/Totals.hlsl(15,5): error OSH5202: WGSL has no 64-bit integer types. Use int or uint (or a uint2 pair), or compile this shader for SPIR-V or MSL only.
```

And a `uint16_t` buffer on WGSL:

```text
/tmp/i16.hlsl(2,57): error OSH5202: WGSL has no 16-bit integer types (only f16). Use int or uint, or compile this shader for SPIR-V or MSL only.
```

The per-target rules are in [SPIR-V](https://recursiveai.org/oneshader/docs/spirv-backend/), [MSL](https://recursiveai.org/oneshader/docs/msl-backend/) and [WGSL](https://recursiveai.org/oneshader/docs/wgsl-backend/), and the generated members under 16-bit and 64-bit members in [Generated bindings](https://recursiveai.org/oneshader/docs/generated-bindings/).
