Published
Edited
Feb 14, 2021
Importers
9 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
table_bits = 5
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
pj_to_float = {
const clz = Math.clz32;
const INF = 2**31;

// Set up a typed array for a 64-bit float, and a view as 2 32-bit integers.
const flt_arr = new Float64Array(1);
const int_arr = new Int32Array(flt_arr.buffer);

const HI = little_endian;
const MANTISSA_MASK = 0x000fffff; // 0000 0000 0000 1111 1111 1111 1111 1111

return function pj_to_float(pj) {
// special case infinity and zero
if (!(pj << 1)) return (pj === 0) ? 0 : Infinity;

// sign: 0 if positive, 1 if negative
const sign = pj >>> 31;

// take absolute value of p, shift left by 1
pj = ((pj - sign) ^ (-sign)) << 1;

// get the sign of the "regime bits", the unary exponent of a pj number
// 1 means exponent >= 0, 0 means exponent < 0
const exp_sign = pj >>> 31;

// count the regime bits
const leading_bits = clz(pj ^ (-exp_sign));

// store a float with the correct mantissa in the typed array
flt_arr[0] = 0x40000000 + ((pj << leading_bits << 1) >>> 2);

// fix the sign and exponent of that float
const exponent = 1023 + (exp_sign * leading_bits << 1) - leading_bits - exp_sign;
int_arr[HI] = (sign << 31) + (exponent << 20) + (int_arr[HI] & MANTISSA_MASK);

return flt_arr[0];
}
}
Insert cell
pj_with_nbits = (PJ_SIZE) => {
// Set up a typed array for 2 64-bit floats, and also a view of it as 4 32-bit
// integers. Use the first float for the input value, and the second for an
// appropriately scaled power of 2 to subtract then add to the value float
// to round it to the appropriate bit
const flt_arr = new Float64Array([0, 1]);
const int_arr = new Int32Array(flt_arr.buffer);

const SIGN_MASK = 0x7fffffff; // 0111 1111 1111 1111 1111 1111 1111 1111
const EXP_MASK = 0x7ff00000; // 0111 1111 1111 0000 0000 0000 0000 0000

// HI = 1 on little endian platforms, 0 on big-endian platforms.
const HI = little_endian, LO = 1 - little_endian;

// use these for rounding values near infinity
const LAST_POW2_CUTOFF = 2 ** (PJ_SIZE - 3) * Math.SQRT2;
const OVERFLOW_CUTOFF = 2 ** (PJ_SIZE - 1);

const INF = 2**31;

// use these in the rounding step
const HALF_EXP = 1022 << 20; // exponent for 0.5
const ROUND_EXP_OFFSET = HALF_EXP + ((56 - PJ_SIZE) << 20);

const float_to_pj = function float_to_pj(flt) {
// put the float into our typed array so we can extract its bits
flt_arr[0] = flt;

// eliminate sign
int_arr[HI] &= SIGN_MASK;

// special case for numbers near infinity
if (flt_arr[0] >= LAST_POW2_CUTOFF) {
if (flt_arr[0] >= OVERFLOW_CUTOFF) return INF;
const output_abs = ((INF >> (PJ_SIZE - 1)) - INF);
return output_abs * ((flt > 0) - (flt < 0));
}

// get exponent
const flt_exp = int_arr[HI] & EXP_MASK;

// round the number to the nearest pj-representable number, ties to even
int_arr[2+HI] = ROUND_EXP_OFFSET + (flt_exp > HALF_EXP) * ((flt_exp - HALF_EXP - 0x80000) << 1)
flt_arr[0] = (flt_arr[0] - flt_arr[1]) + flt_arr[1];

// get exponent again (might have changed after rounding)
const e = ((int_arr[HI] & EXP_MASK) >>> 20) - 1023;

// add 1 to small numbers, as we only want to extract the fraction part
flt_arr[0] += (e < 0);

// fetch 31 bits from the mantissa, put them into "pj"
let pj = (((int_arr[HI] << 12) >>> 1) + (int_arr[LO] >>> 21));

// if e > 0, shift to the right and add the appropriate 1s; make space for sign bit
pj = ((pj >>> ((e + 1) * (e >= 0))) + (INF >> e) * (e >= 0)) >>> 1;

// flip sign for negative input; negative pj numbers have a 2s complement representation
pj *= ((flt > 0) - (flt < 0));
return pj;
}
float_to_pj.inverse = pj_to_float;
return float_to_pj;
}
Insert cell
Insert cell
(1.5 - 2**53) + 2**53
Insert cell
(4.5 - 2**53) + 2**53
Insert cell
Insert cell
Insert cell
// Print binary digits of a 32-bit integer
i2b = (i) =>
("00000000000000000000000000000000"
+ (i >>> 0).toString(2)
).slice(-32)
Insert cell
exponent = {
const arr = new Float64Array(1);
const iarr = new Int32Array(arr.buffer);
const EMASK = 0x7ff00000; // 0111 1111 1111 0000 0000 0000 0000 0000

return (flt) => {
arr[0] = flt;
return ((iarr[1] & EMASK) >>> 20) - 1023;
}
}
Insert cell
sign = {
const arr = new Float64Array(1);
const iarr = new Int32Array(arr.buffer);
const SMASK = 0x80000000; // 0111 1111 1111 0000 0000 0000 0000 0000

return (flt) => {
arr[0] = flt;
return 1 - ((iarr[1] & SMASK) >>> 30);
}
}
Insert cell
mantissa = flt => (2**-32) * (1 << (32 - exponent(flt))) * flt
Insert cell
Insert cell
reverse_bits = {
const M1 = 0x55555555, M2 = 0x33333333, M4 = 0x0F0F0F0F, M8 = 0x00FF00FF;
return function reverse_bits(k) {
k = k>>>1 & M1 | (k & M1)<<1;
k = k>>>2 & M2 | (k & M2)<<2;
k = k>>>4 & M4 | (k & M4)<<4;
k = k>>>8 & M8 | (k & M8)<<8;
return k>>>16 | k<<16;
}
}
Insert cell
// count trailing zeros
ctrz = {
var clz = Math.clz32;
return function ctrz(k) {
// fill all bits to the left of the rightmost 1 bit with 1s
k |= k << 16, k |= k << 8, k |= k << 4, k |= k << 2, k |= k << 1;

// subtract count of leading 1s from 32
return 32 - clz(~k);
}
}
Insert cell
float32 = {
const arr = new Float32Array(1);
const iarr = new Int32Array(arr.buffer);
return function float32(k) {
iarr[0] = k;
return arr[0];
}
}
Insert cell
colors = ({
blue: '#0060ab',
red: '#993b3b'
})
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more