Skip to content

Instantly share code, notes, and snippets.

@mfirmin
Last active February 22, 2024 09:56
Show Gist options
  • Save mfirmin/456e1c6dcf7b0e1bda6e940add32adad to your computer and use it in GitHub Desktop.
Save mfirmin/456e1c6dcf7b0e1bda6e940add32adad to your computer and use it in GitHub Desktop.
Converting Float16 (stored as the bits of a Uint16) into a Javascript Number
// This function converts a Float16 stored as the bits of a Uint16 into a Javascript Number.
// Adapted from: https://gist.github.com/martinkallman/5049614
// input is a Uint16 (eg, new Uint16Array([value])[0])
function float16ToNumber(input) {
// Create a 32 bit DataView to store the input
const arr = new ArrayBuffer(4);
const dv = new DataView(arr);
// Set the Float16 into the last 16 bits of the dataview
// So our dataView is [00xx]
dv.setUint16(2, input, false);
// Get all 32 bits as a 32 bit integer
// (JS bitwise operations are performed on 32 bit signed integers)
const asInt32 = dv.getInt32(0, false);
// All bits aside from the sign
let rest = asInt32 & 0x7fff;
// Sign bit
let sign = asInt32 & 0x8000;
// Exponent bits
const exponent = asInt32 & 0x7c00;
// Shift the non-sign bits into place for a 32 bit Float
rest <<= 13;
// Shift the sign bit into place for a 32 bit Float
sign <<= 16;
// Adjust bias
// https://en.wikipedia.org/wiki/Half-precision_floating-point_format#Exponent_encoding
rest += 0x38000000;
// Denormals-as-zero
rest = (exponent === 0 ? 0 : rest);
// Re-insert sign bit
rest |= sign;
// Set the adjusted float32 (stored as int32) back into the dataview
dv.setInt32(0, rest, false);
// Get it back out as a float32 (which js will convert to a Number)
const asFloat32 = dv.getFloat32(0, false);
return asFloat32;
}
// This is especially useful if you need to access the data stored in a WebGL HALF_FLOAT RenderTarget
// Example (using THREE.js):
const target = new THREE.WebGLRenderTarget(width, height, {
format: THREE.RGBAFormat,
type: THREE.HalfFloatType,
stencilBuffer: false,
});
// ... render to target ...
const dataFloat16 = new Uint16Array(4);
renderer.readRenderTargetPixels(target, x, y, 1, 1, dataFloat16);
// Convert the float16 data stored in each of the R,G,B,A channels to js Numbers
const data = [
float16ToNumber(dataFloat16[0]),
float16ToNumber(dataFloat16[1]),
float16ToNumber(dataFloat16[2]),
float16ToNumber(dataFloat16[3]),
];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment