Resources Numerical accuracy Calculation policy

Half floats in USD

Converts between 16-bit half floats and 32-bit floats for USD half types.

How the precision is kept

Every half value converts exactly to a 32-bit float through a 65536 entry Float32Array table. The reverse rounds a number to the nearest half, ties to even, but it first stores the input in a 32-bit float.

Open item. Round the 64-bit input to half directly instead of through a 32-bit value.

Calculations under this policy

2 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.

usd/util.js CadCraft 0.8.2 32-bit in part 2 calculations Reviewed

Tests None found by the review

Half to float

32-bit in part

The value of a 16-bit half float.

v = ± (1 + m ÷ 1024) × 2^(e − 15)

Looks the value up in the table built as s × m × 2⁻²⁴ (subnormal) or s × (1 + m ÷ 1024) × 2^(e − 15); every half is exactly representable in 32-bit, so the table holds exact values.

  • usd/util.js
  • lines 45 to 48
  • CadCraft 0.8.2
for (let h = 0; h < 65536; h++) {
  const s = h & 0x8000 ? -1 : 1, e = (h >> 10) & 0x1f, m = h & 0x3ff;
  HALF[h] = e === 0 ? s * m * 2 ** -24 : e === 31 ? (m ? NaN : s * Infinity) : s * (1 + m / 1024) * 2 ** (e - 15);
}

Float to half

32-bit in part

The 16-bit half closest to a number.

F32[0] = f rounds the input to 32-bit first. Normal numbers round the 13 dropped mantissa bits to nearest even and a carry moves into the exponent; subnormals scale by 2^24 and round half to even; overflow gives infinity.

  • usd/util.js
  • lines 50 to 66
  • CadCraft 0.8.2
export function floatToHalf(f) {
  F32[0] = f;
  const x = U32[0], sign = (x >>> 16) & 0x8000, e = (x >>> 23) & 0xff;
  let m = x & 0x7fffff;
  if (e === 0xff) return sign | 0x7c00 | (m ? 0x200 | (m >>> 13) : 0);
  const he = e - 127 + 15;
  if (he >= 31) return sign | 0x7c00;
  if (he <= 0) {
    // subnormal half: the value in units of 2^-24, rounded to nearest even (exact: |f| < 2^-14 has few bits here)
    const u = Math.abs(F32[0]) * 16777216, r = Math.floor(u), rest = u - r;
    return sign | (rest > 0.5 || (rest === 0.5 && (r & 1)) ? r + 1 : r);
  }
  const r = m >>> 13, rest = m & 0x1fff;
  let out = sign | (he << 10) | r;
  if (rest > 0x1000 || (rest === 0x1000 && (r & 1))) out++;
  return out;
}

In detail

Method
Table of all 65536 half values for halfToFloat; bit rounding of the 32-bit pattern (and a scaled round for subnormals) for floatToHalf.
Number types
Float32Array(1) with a Uint32Array view, Float32Array(65536) table; 64-bit input is rounded to 32-bit first.
Tolerances
None.
Limits
A 64-bit input that is just off a half midpoint can be pushed onto it by the 32-bit step and then round to even, which is double rounding.

Approach: 32-bit step, flagged

One step stores or exchanges 32-bit values that reach a number a person reads. It is listed here with its open item, and the step is either moved to 64-bit or the app will warn where the number is read.

Every policy that takes this approach