preface
Base and bit operations are rarely touched in normal code, but that doesn’t mean we can’t learn them. As a programmer, these are the basics. Don’t panic if you haven’t learned this before, it’s not going to be hard. In this article you will learn:
- Hexadecimal conversion
- Bitwise operators
- Javascript base conversion
- Implement base conversion manually
Hexadecimal conversion
The following uses common decimal and binary conversions as examples. Other base conversions are similar, so you can think about them for yourself.
Decimal to binary
When counting according to the rule of “one into ten”, every ten identical units form a higher unit next to it. This counting method is called decimal counting, or decimal for short. This is the notation we use most often.
The integer
Integers are converted to binary using “mod two, reverse order”. Here is an example of how 18 is converted to binary:
// Divide by two, the remainder is 18/2 = 9... 0 9/2 = 4... 1 4/2 = 2... 0 2/2 = 1... 0 1/2 = 0... 1 // In reverse order 10010Copy the code
It’s as simple as that. Invert the remainder, and you get the binary representation of 18
The decimal
Decimals are used in “double the whole, order”, due to different methods need to be calculated separately. Here is an example of converting 16.125 to binary:
16/2 = 8... 0 8/2 = 4... 0 4/2 = 2... 0 2/2 = 1... 0 1/2 = 0... 1 0.125 * 2 = 0.25 0.25 * 2 = 0.5 0.5 * 2 = 1 10000.001Copy the code
The result of multiplying decimals, taking the integer order of the result, to obtain the binary representation of decimal places
Binary to decimal
When counting according to the law of “two into one”, every two identical units form a higher unit adjacent to it. This counting method is called binary counting, or binary for short. In binary counting, only two separate symbols, “0” and “1”, are used.
The integer
Integers are added by weight, in which binary numbers are first written in weighted coefficient expansion and then summed up according to decimal addition rules. Here is an example of 101010 converted decimal:
2 ^ 2 ^ ^ 5 4 3 2 2 2 ^ ^ 1 ^ 2 0 0 1 0 0 1 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 32 + 0 + 8 + 0 + 2 + 0 = 42Copy the code
From the right, two to the zero, two to the first, two to the second… , just take the number of bits 1 and add them to get decimal.
The decimal
10110.11 To decimal:
2 ^ 4 2 ^ 3 ^ ^ 1 ^ 2 2 2 2 2 ^ 2 ^ 0-1-2 0 1 1 0. 1 1 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 16 + 0 + 4 + 2 + 0 + 0.5 + 0.25 = 22.75Copy the code
Bitwise operators
Bitwise operators treat their operands as a sequence of 32-bit bits (zeros and ones), with the first 31 bits representing the value of the integer, the 32nd bit representing the symbol of the integer, 0 representing a positive number, and 1 representing a negative number. For example, the decimal number 18 is 10010 in binary. Bitwise operators operate on the binary form of numbers, but the return value is still a standard JavaScript value.
Bitwise AND (AND)
For each bit, the result is 1 only if the corresponding bit bits of both operands are 1; otherwise, it is 0.
Usage: A & B.
9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
--------------------------------
14 & 9 (base 10) = 00000000000000000000000000001000 (base 2) = 8 (base 10)
Copy the code
A & 1 can be used to determine if a number is even or odd
function assert(n) {
return n & 1 ? "Odd" : "Even"
}
assert(3) / / odd
Copy the code
Because the binary of odd numbers ends with 1, and the binary of 1 ends with 1, the & operator yields 1
Bitwise OR (OR)
For each bit, the result is 1 if there is at least one 1 in the corresponding bit of the two operands, and 0 otherwise.
Usage: a | b
9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
--------------------------------
14 | 9 (base 10) = 00000000000000000000000000001111 (base 2) = 15 (base 10)
Copy the code
Will float down integer to integer, you can use a | 0
12.1 | 0 / / 12
12.9 | 0 / / 12
Copy the code
Bitwise XOR
For each bit, the result is 1 if there is one and only one 1 in the corresponding bit of the two operands, and 0 otherwise.
A ^ B
9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
--------------------------------
14 ^ 9 (base 10) = 00000000000000000000000000000111 (base 2) = 7 (base 10)
Copy the code
Bitwise NOT
Invert the operand’s bits, i.e., 0 becomes 1,1 becomes 0.
Usage: ~ a
9 (base 10) = 00000000000000000000000000001001 (base 2)
--------------------------------
~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)
Copy the code
A floating-point number is rounded down to an integer by two inversion operations
~ ~16.125 / / 16~ ~16.725 / / 16
Copy the code
Left shift
Shift the binary form of A to the left by b (< 32) bits, filling the right with zeros.
A << B
9 (base 10): 00000000000000000000000000001001 (base 2)
--------------------------------
9 << 2 (base 10): 00000000000000000000000000100100 (base 2) = 36 (base 10)
Copy the code
Moving one digit to the left is equivalent to multiplying the original number by 2, and using this feature, achieving 2 to the n power:
function power(n) {
return 1 << n
}
power(3) / / 8
Copy the code
There’s a sign shift to the right
Move the binary representation of A to the right by b (< 32) bits, discarding the removed bits.
A >> B
9 (base 10): 00000000000000000000000000001001 (base 2)
--------------------------------
9 >> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)
Copy the code
In contrast, -9 >> 2 gives -3 because the sign is preserved.
-9 (base 10): 11111111111111111111111111110111 (base 2)
--------------------------------
-9 >> 2 (base 10): 11111111111111111111111111111101 (base 2) = -3 (base 10)
Copy the code
Instead of moving left, you move right one bit and divide by 2
64 >> 1 / / 32
Copy the code
Unsigned shift to the right
Move the binary representation of A to the right by b (< 32) bits, discard the removed bits, and fill in the left side with zeros.
A >>> B
For non-negative numbers, 9 >>>2 is the same as 9 >>2
9 (base 10): 00000000000000000000000000001001 (base 2)
--------------------------------
9 >>> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)
Copy the code
For negative numbers, the result is quite different, because >>> does not retain the sign, and when negative numbers move unsigned to the right, they are padded with 0
-9 (base 10): 11111111111111111111111111110111 (base 2)
--------------------------------
-9 >>> 2 (base 10): 00111111111111111111111111111101 (base 2) = 1073741821 (base 10)
Copy the code
You can use an unsigned right shift to determine whether a number is positive or negative
function isPos(n) {
return (n === (n >>> 0))?true : false;
}
isPos(- 1); // false
isPos(1); // true
Copy the code
Although -1 >>> 0 does not shift right, the -1 binary has become a positive binary, and -1 >>> 0 results in 4294967295
Javascript base conversion
toString
ToString is often used to convert a variable to a string, or to determine the type of a variable, for example:
let arr = []
Object.prototype.toString.call(arr) // [object Array]
Copy the code
You probably didn’t think toString could be used for base conversions. Here’s an example:
(18).toString(2) // 10010 (Base 2)
(18).toString(8) // 22 (base 8)
(18).toString(16) // 12 (base 16)
Copy the code
The value is an integer ranging from 2 to 36. If this parameter is omitted, the cardinality 10 is used. This parameter can be interpreted as the converted base representation.
parseInt
ParseInt is usually used to round numbers. It can also pass arguments to base conversions, as shown in the following example:
parseInt(10010.2) // 18 (base 10)
parseInt(22.8) // 18 (base 10)
parseInt(12.16) // 18 (base 10)
Copy the code
The second argument represents the cardinality of the number to be parsed, which is between 2 and 36. If this parameter is omitted or its value is 0, the number is parsed on the basis of 10. If this parameter is less than 2 or greater than 36, parseInt returns NaN.
Remember an interview question that went something like this:
// Ask: The result returned
[1.2.3].map(paseInt)
Copy the code
Now, let’s take a step by step look at what happens in the process.
parseInt(1.0) // When the cardinality is 0, the result is 1
parseInt(2.1) // The cardinality does not fit the range from 2 to 36, and the result is NaN
parseInt(3.2) // Here we parse in base 2, but 3 is clearly not a binary representation, so the result is NaN
// The result is
[1.NaN.NaN]
Copy the code
Implement base conversion manually
Although JavaScript has built-in base conversion functions for us, implementing base conversion manually helps us understand the process and improve our logic. It’s also a great exercise for beginners. The following is a simple conversion of non-negative integers.
Decimal to binary
Based on “divide two take more than” idea
function toBinary(value) {
if (isNaN(Number(value))) {
throw `${value} is not a number`
}
let bits = []
while (value >= 1) {
bits.unshift(value % 2)
value = Math.floor(value / 2)}return bits.join(' ')}Copy the code
use
toBinary(36) / / 100100
toBinary(12) / / 1100
Copy the code
Binary to decimal
Based on the idea of “power addition”
function toDecimal(value) {
let bits = value.toString().split(' ')
let res = 0
while (bits.length) {
let bit = bits.shift()
if (bit == 1) {
// ** is the power operator, e.g. 2**3 is 8
res += 2 ** bits.length
}
}
return res
}
Copy the code
use
toDecimal(10011) / / 19
toDecimal(11111) / / 33
Copy the code
Write in the last
This article introduces the relevant knowledge of base and bit operation, aiming at reviewing the old and learning the new. We only need a rough idea, because we really don’t use it much in development, at least I’ve only used ~~ to round things. Rounding operations such as ~~ should be used sparingly as they may affect code readability for other developers.