DEV Community

Discussion on: About bitwise operations

Collapse
 
peerreynders profile image
peerreynders • Edited

Passing FtoJ as a subgroup instead would've logged only F with a value of 100000.

Only once the for condition is fixed to check for i <= group instead.
Also const group = (values & subgroup) >>> 0; is needed — otherwise bit 31 won't be recognized.

taking the opportunity to introduce >>> too, which is the unsigned right shift operator.

Given the context I think n >>> 0 needs an introduction as well, i.e. in general bitwise operators yield Int32 — however >>> yields a Uint32 (hence the name; Alternate implementations of ToUint32 and ToInt32).

Please note that 31 is the maximum possible bit value here, while the previous version would've reached up to 53 without issues.

Given that the previous version uses group & i it's just as bound to the 32 bit limit. That said the limit can be widened to 53 bits without resorting to BigInt.

const HIGH_BASE = 0x1_0000_0000;
const HIGH_SHIFT = [(value) => value, (value) => value * HIGH_BASE];

function* eachValue(values, queryMask = Number.MAX_SAFE_INTEGER) {
  const maskSplit = splitLowHigh(queryMask);
  const valuesSplit = splitLowHigh(values);

  for (let n = 0; n < HIGH_SHIFT.length; n += 1)
    for (
      let i = 0,
        field = toUint32(valuesSplit[n] & maskSplit[n]),
        shift = HIGH_SHIFT[n];
      field > 0;
      i += 1, field >>>= 1
    )
      if (field & 1) yield shift((1 << i) >>> 0);
}

function splitLowHigh(value) {
  const low = value % HIGH_BASE;
  return [low, (value - low) / HIGH_BASE];
}

function toUint32(value) {
  return value >>> 0;
}

const KEY_BASE = 'A'.charCodeAt(0);
const KEY_INTERVAL = 26;
const c = prepareRecord();

const AtoE = 0b1_1111;

for (const value of eachValue(c.A | c.D | c.F, AtoE))
  console.log(value.toString(2).padStart(5, '0'));

// 00001
// 01000

for (const value of eachValue(c.AF + c.AG + c.BA, Number.MAX_SAFE_INTEGER))
  console.log(value.toString(16).padStart(14, '0'));

// 00000080000000
// 00000100000000
// 10000000000000

function prepareRecord() {
  const FIELD_END = [32, 53];
  const record = {};
  let i = 0;
  for (let n = 0; n < HIGH_SHIFT.length; n += 1)
    for (
      let value = 1, endIndex = FIELD_END[n], shift = HIGH_SHIFT[n];
      i < endIndex;
      value = toUint32(value << 1), i += 1
    )
      record[indexToKey(i)] = shift(value);

  return record;
}

function indexToKey(i) {
  let key = '';
  do {
    const r = i % KEY_INTERVAL;
    key = String.fromCharCode(KEY_BASE + r) + key;
    i = (i - r) / KEY_INTERVAL - 1;
  } while (i > -1);

  return key;
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
webreflection profile image
Andrea Giammarchi

Awesome review, thank you! I have addressed pretty much all your points but I won't go down the 1 << 31 and beyond rabbit hole, although you are right the post was misleading, and I actually had to (2 ** 31) & 1 to indeed realize that all operations are broken regardless the fact the integer is theoretically safe, as Number.MAX_SAFE_NUMBER would suggest. I guess I would've learned it the hard way once added more values to some code I'm working on, so apologies for that misleading bit, now removed, and thanks again for the hints 👍