Last active
August 17, 2024 10:52
-
-
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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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