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.jsCadCraft 0.8.2 32-bit in part2 calculationsReviewed
TestsNone 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.