bit-manipulation

An ES module providing functions for setting and clearing bits (and doing any bitwise operations) in Numbers up to 53 bits and in BigInts of any bit-width. In addition there's also functions to manipulate the bits in JavaScript's 64-bit floats and represe

Usage no npm install needed!

<script type="module">
  import bitManipulation from 'https://cdn.skypack.dev/bit-manipulation';
</script>

README

bit-manipulation

Description

An ES module providing functions for setting and clearing bits (and doing any bitwise operations) in Numbers up to 53 bits and in BigInts of any bit-width. In addition there's also functions to manipulate the bits in JavaScript's 64-bit floats and representing integers (in two's complement format) in binary or hex. It also has functions to check if any bits are set or not.

If you're new to JavaScript it might sound strange to have a library for doing bitwise operations since JavaScript has operators for this inbuilt in the language. But the problem is that the inbuilt operators converts any operands into 32-bit signed integers, hence they can't return a result that is over 32 bits AND they treat bit 32 as the sign bit (although >>> 0 can be used to get an unsigned result).

Funding

If you find this useful then please consider helping me out (I'm jobless and sick). For more information visit my GitHub profile.

How to use

Install using NPM

npm i bit-manipulation

Import the ES module into Node.js

import * as bm from 'bit-manipulation'

Got problems using ES modules? This might help or read the documentation.

Import the ES module into the browser or Deno

import * as bm from '/node_modules/bit-manipulation/source/bitManipulation.js'

Example

import * as bm from 'bit-manipulation'
const log = console.log // yes, this is handy

// Classical use of (some of) the functions in this library
let v = 0b1000_0000
v = bm.set(v, 1,2,3,4) // set these bits in v and return the result
log(bm.numberToBinary(v))
v = bm.xor(v, 0xFF) // xor v with 0xFF
log(bm.numberToBinary(v))
log(bm.isSet(v, 1)) // check if the LSB is set
log(v) // the value in base 10

log() // Wrapping a value in bm.Serial allows us to do the same operations on it in series in a more convenient way
v = bm.serial(0b1000_0000)
.set(1,2,3,4).log('bin')
.xor(0xFF).log('bin')
log(v.isSet(1))
log(v.out()) // then .out() returns the result

log() // You can go mad with that
bm.serial(0).set(1,8).xor(0xFF).and(0b1111).or(0xF0).flip(1,8).not(8).log('bin')

log() // And then debug your madness for better clarity
bm.serial(0).set(1,8).log(2).xor(0xFF).log(2).and(0b1111).log(2).or(0xF0).log(2).flip(1,8).log(2).not(8).log(2)

log() // There's lots of fun to be had
log(bm.numberToHex(bm.reverseBitOrder(0xD00D, 16), 2)) 

log() // And yes, you can set bits higher than bit 32
log(bm.numberToBinary(bm.set(0, 33, 53), 64))
log(bm.numberToBinary(bm.set(0n, 33, 64))) // even higher than bit 53 if using BigInts
// And bitwise operations works fine on them
log(bm.numberToBinary(bm.or(bm.set(0, 33, 53), 1), 64))
log(bm.numberToBinary(bm.or(bm.set(0n, 33, 64), 1)))

log() // Did you ever want to mess around with the bits in a 64-bit float? Well, now you can!
let bits = bm.float.toBits(1.23456) // returns an overview of any set bits
log(bits)
log(bm.float.fromBits(bits)) // and can be used to create a float
let float = bm.float.set(1.23456, {signBit: true}) // set the sign bit
log(float)
float = bm.float.clear(float, {signBit: true}) // clear it
log(bm.float.set(float, {significand: [51]})) // and set bit 51 in the significand / mantissa
// you can even set bits using values
float = bm.float.fromBits({exponent: 0b1100_0001, significand: 0xF_FFFF_FFFF_FFFF})
log(bm.float.toBits(float)) // check that it worked correctly
log(float) // also let's check what that float looks like

log() // There's also an educational value in learning about two's complement format
log(bm.negativeIntegerFromBits(1)) // the bits to set to 0
log(bm.negativeIntegerFromValue(0b10)) // same as this (where the MSB is the sign bit)

log() // And I'm sure that you at least once wanted to know the bit length/width of a number
log(bm.bitLength(0xFFFF_FFFF))
log(bm.bitLength(0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFFn))

log('Please tell your mom and dad about this! 🙂')

Console output from example (for those without a computer):

0b10001111
0b01110000
false
112

0b10001111
0b01110000
false
112

0b10000000

0b10000001
0b01111110
0b00001110
0b11111110
0b01111111
0b10000000

0xB00B

0b00000000_00010000_00000000_00000001_00000000_00000000_00000000_00000000
0b10000000_00000000_00000000_00000001_00000000_00000000_00000000_00000000n
0b00000000_00010000_00000000_00000001_00000000_00000000_00000000_00000001
0b10000000_00000000_00000000_00000001_00000000_00000000_00000000_00000001n

{
  signBit: false,
  exponent: [
    1, 2, 3, 4,  5,
    6, 7, 8, 9, 10
  ],
  significand: [
     4,  5,  6, 10, 13, 14, 17, 18,
    19, 20, 24, 27, 28, 29, 30, 31,
    32, 33, 39, 40, 47, 48, 49, 50
  ]
}
1.23456
-1.23456
1.48456
{
  signBit: false,
  exponent: [ 1, 7, 8 ],
  significand: [
     1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,
    12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
    23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
    34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
    45, 46, 47, 48, 49, 50, 51, 52
  ]
}
2.793402995719818e-250

-2
-2

32
96

Auto-generated API documentation (from JSDoc)

Modules

bit-manipulation

Objects

float : object

I failed finding any way for jsdoc-to-markdown to document the float namespace as being part of the bit-manipulation module, but it is...

bit-manipulation

bm.Serial

A class allowing multiple bitwise operations done in serial order on a value (for convenience). Call out() to return the result when done. This class actually has most of the functions in this library as its methods.

Kind: static class of bit-manipulation

new exports.Serial(value)

Param Type Description
value number | bigint Enter the value to operate on, must be an integer.

serial.x(func)

Manipulate or read the value using your own function, return nothing if no change is to be made or return the new value.

Kind: instance method of Serial

Param Type Description
func function A function which will receive the value.

serial.out([base]) ⇒ number | bigint | string

Returns the value operated on.

Kind: instance method of Serial

Param Type Description
[base] number | string The base to use. Defaults to base 10, also supports base 2 and 16.

serial.log([base])

A shortcut to logging the value using console.log. But with options for using a binary or hexadecimal representation.

Kind: instance method of Serial

Param Type Description
[base] number | string The base to use. Defaults to base 10, also supports base 2 and 16.

serial.not(bitLength)

Bitwise NOT (invert the bits). If not specifying a bitLength to operate within then inverting the bits using not will turn any positive number into a negative one because the sign bit (which is virtual when using this library by the way) will be inverted as well.

Kind: instance method of Serial

Param Type
bitLength number

serial.reverseBitOrder(bitLength)

Reverse the bit order up to the bitLength supplied, the result will be capped to this size.

Kind: instance method of Serial

Param Type Description
bitLength number The wanted bit length of the result (the position of the most significant bit).

serial.rShift(offset)

Shift the bits towards the right (the least significant side) offset amount of bits. Bits shifted in from the left will be set to 0.

Kind: instance method of Serial

Param Type Description
offset number How far to shift the bits.

serial.lShift(offset)

Shift the bits towards the left (the most significant side) offset amount of bits. Bits shifted in from the right will be set to 0.

Kind: instance method of Serial

Param Type Description
offset number How far to shift the bits.

serial.and()

Bitwise AND

Kind: instance method of Serial

serial.or()

Bitwise OR

Kind: instance method of Serial

serial.xor()

Bitwise XOR

Kind: instance method of Serial

serial.set(...bits)

Set (as in setting them to 1) these bits (others retain their state).

Kind: instance method of Serial

Param Type Description
...bits number Which bits to set, where 1 means the least significant bit.

serial.clear(...bits)

Clear (as in setting them to 0) these bits (others retain their state).

Kind: instance method of Serial

Param Type Description
...bits number Which bits to clear, where 1 means the least significant bit.

serial.flip(...bits)

Flip (or toggle) these bits (others retain their state).

Kind: instance method of Serial

Param Type Description
...bits number Which bits to flip, where 1 means the least significant bit.

serial.isSet(...bits) ⇒ boolean

Check if all of these bits are set (as in having a state of 1).

Kind: instance method of Serial
Returns: boolean - True or false

Param Type Description
...bits number Which bits to check.

serial.isAnySet(...bits) ⇒ boolean

Check if any of these bits are set (as in having a state of 1).

Kind: instance method of Serial
Returns: boolean - True or false

Param Type Description
...bits number Which bits to check.

serial.isNotSet(...bits) ⇒ boolean

Check if all of these bits are not set (as in having a state of 0).

Kind: instance method of Serial
Returns: boolean - True or false

Param Type Description
...bits number Which bits to check.

serial.bitLength() ⇒ number

Returns the number of bits needed to be able to represent the value. For negative numbers this does not include the sign bit.

Kind: instance method of Serial
Returns: number - The bit length of the value.

bm.set(value, ...bits) ⇒ number | bigint

Set (as in setting them to 1) these bits in the value (others retain their state).

Kind: static method of bit-manipulation

Param Type Description
value number | bigint The value to operate on.
...bits number Which bits to set, where 1 means the least significant bit.

bm.clear(value, ...bits) ⇒ number | bigint

Clear (as in setting them to 0) these bits in the value (others retain their state).

Kind: static method of bit-manipulation

Param Type Description
value number | bigint The value to operate on.
...bits number Which bits to clear, where 1 means the least significant bit.

bm.flip(value, ...bits) ⇒ number | bigint

Flip (or toggle) these bits in the value (others retain their state).

Kind: static method of bit-manipulation

Param Type Description
value number | bigint The value to operate on.
...bits number Which bits to flip, where 1 means the least significant bit.

bm.isAnySet(value, ...bits) ⇒ boolean

Check if any of these bits are set (as in having a state of 1) in the value.

Kind: static method of bit-manipulation
Returns: boolean - True or false

Param Type Description
value number | bigint The value to check.
...bits number Which bits to check.

bm.isSet(value, ...bits) ⇒ boolean

Check if all of these bits are set (as in having a state of 1) in the value.

Kind: static method of bit-manipulation
Returns: boolean - True or false

Param Type Description
value number | bigint The value to check.
...bits number Which bits to check.

bm.isNotSet(value, ...bits) ⇒ boolean

Check if all of these bits are not set (as in having a state of 0) in the value.

Kind: static method of bit-manipulation
Returns: boolean - True or false

Param Type Description
value number | bigint The value to check.
...bits number Which bits to check.

bm.lShift(value, offset) ⇒ number | bigint

Shift the bits in the value towards the left (the most significant side) offset amount of bits. Bits shifted in from the right will be set to 0.

Kind: static method of bit-manipulation
Returns: number | bigint - The result.

Param Type Description
value number | bigint The value to operate on.
offset number How far to shift the bits.

bm.rShift(value, offset) ⇒ number | bigint

Shift the bits in the value towards the right (the least significant side) offset amount of bits. Bits shifted in from the left will be set to 0.

Kind: static method of bit-manipulation
Returns: number | bigint - The result.

Param Type Description
value number | bigint The value to operate on.
offset number How far to shift the bits.

bm.xor(value1, value2) ⇒ number | bigint

Bitwise XOR (exclusive or) value1 with value2 and return the result.

Kind: static method of bit-manipulation

Param Type
value1 number | bigint
value2 number | bigint

bm.or(value1, value2) ⇒ number | bigint

Bitwise OR value1 with value2 and return the result.

Kind: static method of bit-manipulation

Param Type
value1 number | bigint
value2 number | bigint

bm.and(value1, value2) ⇒ number | bigint

Bitwise AND value1 with value2 and return the result.

Kind: static method of bit-manipulation

Param Type
value1 number | bigint
value2 number | bigint

bm.not(value, [bitLength]) ⇒ number | bigint

Bitwise NOT value (invert the bits) and return the result. If not specifying a bitLength to operate within then inverting the bits using not will turn any positive number into a negative one because the sign bit (which is virtual when using this library by the way) will be inverted as well.

Kind: static method of bit-manipulation
Returns: number | bigint - The result.

Param Type Description
value number | bigint The value to operate on.
[bitLength] number The bit length to operate within, set it to restrict the NOT operation to only the first bitLength bits of the value. Then you can not in any way invert the sign bit.

bm.reverseBitOrder(value, bitLength) ⇒ number | bigint

Reverse the bit order of the value up to the bitLength supplied, the returned value will be capped to this size.

Kind: static method of bit-manipulation
Returns: number | bigint - The result.

Param Type Description
value number | bigint The value to operate on.
bitLength number The wanted bit length of the returned value (the position of the most significant bit).

bm.bitLength(value) ⇒ number

Returns the number of bits needed to be able to represent the value. For negative numbers this does not include the sign bit.

Kind: static method of bit-manipulation
Returns: number - The bit length of the value.

Param Type Description
value number | bigint The value to measure the length in bits of.

bm.bitmask(...bits) ⇒ number | bigint

Create a bitmask (which is a value with bits in these positions set). If setting bits over bit 32 then it returns a BigInt.

Kind: static method of bit-manipulation

Param Type Description
...bits number The bit positions of any set (as in 1) bits.

bm.bitmask_allSet(numBits) ⇒ number | bigint

Create a bitmask with a length of numBits where all the bits are set, if numBits is more than 53 then a BigInt will be returned.

Kind: static method of bit-manipulation
Returns: number | bigint - The bitmask.

Param Type Description
numBits number Defines how wide (in bits) the bitmask is.

Example

bitmask_allSet(32) == 0xFFFF_FFFF

bm.integerFromBits(...bits) ⇒ number | bigint

Create a positive integer with only these bits set. If setting bits over bit 53 (> Number.MAX_SAFE_INTEGER) then a BigInt is returned.

Kind: static method of bit-manipulation

Param Type Description
...bits number Which bits to set, where 1 means the least significant bit.

bm.negativeIntegerFromBits(...bits) ⇒ number | bigint

Create a negative integer (two's complement) from which bit positions should be set to 0.

Kind: static method of bit-manipulation

Param Type Description
...bits number Which bits to set to 0, where 1 means the least significant bit.

bm.negativeIntegerFromValue(value) ⇒ number | bigint

Create a negative integer (two's complement) from the bits in a value. One use case could be that you read the bits into an unsigned integer, but you actually wanted those bits to be represented as a negative number. Or educational use.

Kind: static method of bit-manipulation

Param Type Description
value number | bigint The value with bits representing a negative number in two's complement format.

bm.serial(value) ⇒ Serial

If you want to do multiple bitwise operations (in serial order) then this will wrap the value in a class which has most of the functions in this library as its methods. Call out() to return the result when done.

Kind: static method of bit-manipulation

Param Type
value number | bigint

Example

serial(value).rShift(32).and(0xFFFF_FFFF).out()

bm.numberToHex(number, [paddingByteSize], [grouping], [showPrefix]) ⇒ string

Converts any Number or BigInt into a hexadecimal representation.

Numbers with a fractional part will represented in the IEEE 754 double-precision binary format. Where the first bit is the sign bit, followed by the exponent (11 bits) and the significand / mantissa (52 bits).

Other numbers (integers) will be represented in the two's complement format. Where negative numbers are actually displayed correctly compared to when using (number).toString(16).

Kind: static method of bit-manipulation
Returns: string - The hexadecimal representation.

Param Type Default Description
number number The number to convert to hex.
[paddingByteSize] number 4 How many bytes to align the output with.
[grouping] number 4 Group the output into chunks of this size.
[showPrefix] boolean true Whether to prefix the output with 0x or not.

bm.numberToBinary(number, [paddingBits], [grouping], [showPrefix]) ⇒ string

Converts any Number or BigInt into a binary representation.

Numbers with a fractional part will represented in the IEEE 754 double-precision binary format. Where the first bit is the sign bit, followed by the exponent (11 bits) and the significand / mantissa (52 bits).

Other numbers (integers) will be represented in the two's complement format. Where negative numbers are actually displayed correctly compared to when using (number).toString(2).

Kind: static method of bit-manipulation
Returns: string - The binary representation.

Param Type Default Description
number number The number to convert to binary.
[paddingBits] number 8 How many bits to align the output with.
[grouping] number 4 Group the output into chunks of this size.
[showPrefix] boolean true Whether to prefix the output with 0b or not.

float : object

I failed finding any way for jsdoc-to-markdown to document the float namespace as being part of the bit-manipulation module, but it is...

Kind: global namespace

float.fromBits ⇒ number

Create a (64-bit double precision) floating point number by setting the bits in its exponent and significand (often called the mantissa). Set the signBit to turn the float into a negative number. For help see this: https://en.wikipedia.org/wiki/Double-precision_floating-point_format

Kind: static property of float
Returns: number - A 64-bit float.

Param Type Description
parts object The object holding information about which bits in the float is set.
parts.exponent number | Array.<number> The exponent.
parts.significand number | bigint | Array.<number> The fractional part.
parts.signBit boolean Set it to make the float negative.

float.toBits ⇒ object

Convert a (64-bit double precision) floating point number into an object with information about which bits are set. The exponent and significand (often called the mantissa) are returned as arrays containing the bit positions of any set bits (as in bits that are 1).

Kind: static property of float
Returns: object - {signBit, exponent, significand}

Param Type
float number

float.set ⇒ number

Set bits (as in setting them to 1) in the float supplied. This is done using an object with information about which bits to set in the exponent and the significand. Set the signBit to turn the float into a negative number.

Kind: static property of float
Returns: number - A 64-bit float.

Param Type Description
float number The float to manipulate.
parts object The object holding information about which bits to set.
parts.exponent number | Array.<number> The exponent.
parts.significand number | bigint | Array.<number> The fractional part.
parts.signBit boolean Set it to make the float negative.

float.clear ⇒ number

Clear bits (as in setting them to 0) in the float supplied. This is done using an object with information about which bits to clear in the exponent and the significand.

Kind: static property of float
Returns: number - A 64-bit float.

Param Type Description
float number The float to manipulate.
parts object The object holding information about which bits to clear.
parts.exponent number | Array.<number> The exponent.
parts.significand number | bigint | Array.<number> The fractional part.
parts.signBit boolean Clear it to make a negative float positive.

End of readme

Death is not the end of your consciousness, it's the expansion of it.