This commit is contained in:
Ricardo Fernández Serrata 2024-07-17 10:41:55 +09:00 committed by GitHub
commit 32405871c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 87 additions and 120 deletions

View File

@ -1,7 +1,9 @@
import isPowerOfTwo from '../isPowerOfTwo'; import isPowerOfTwo from '../isPowerOfTwo';
describe('isPowerOfTwo', () => { describe('isPowerOfTwo', () => {
it('should detect if the number is power of two', () => { it('should detect if the int is power of two', () => {
expect(isPowerOfTwo(-32)).toBe(false);
expect(isPowerOfTwo(-1)).toBe(false);
expect(isPowerOfTwo(1)).toBe(true); expect(isPowerOfTwo(1)).toBe(true);
expect(isPowerOfTwo(2)).toBe(true); expect(isPowerOfTwo(2)).toBe(true);
expect(isPowerOfTwo(3)).toBe(false); expect(isPowerOfTwo(3)).toBe(false);
@ -16,5 +18,6 @@ describe('isPowerOfTwo', () => {
expect(isPowerOfTwo(32)).toBe(true); expect(isPowerOfTwo(32)).toBe(true);
expect(isPowerOfTwo(127)).toBe(false); expect(isPowerOfTwo(127)).toBe(false);
expect(isPowerOfTwo(128)).toBe(true); expect(isPowerOfTwo(128)).toBe(true);
expect(isPowerOfTwo(2 ** 30)).toBe(true);
}); });
}); });

View File

@ -1,7 +1,39 @@
/** /**
* @param {number} number * @param {number} number
* @return bool * @return {boolean}
*/ */
export default function isPowerOfTwo(number) { export default function isPowerOfTwo(number) {
// 1 (2^0) is the smallest power of two.
if (number < 1) {
return false;
}
/*
* Powers of two in binary look like this:
* 1: 0001
* 2: 0010
* 4: 0100
* 8: 1000
*
* Note that there is always exactly 1 bit set. The only exception is with a signed integer.
* e.g. An 8-bit signed integer with a value of -128 looks like:
* 10000000
*
* So after checking that the number is greater than zero, we can use a clever little bit
* hack to test that one and only one bit is set.
*
* For example for number `8` the operations will look like:
*
* 1000
* - 0001
* ----
* 0111
*
* 1000
* & 0111
* ----
* 0000
*
* References: http://www.graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
*/
return (number & (number - 1)) === 0; return (number & (number - 1)) === 0;
} }

View File

@ -1,51 +1,15 @@
# Is a power of two # Is a power of two
Given a positive integer, write a function to find if it is Given a Real number, write a function to find if it is an integer power of the number 2, or not.
a power of two or not.
**Naive solution** **Standard solution**
In naive solution we just keep dividing the number by two We just keep dividing the number by two, unless the number becomes `1` and every time we do so,
unless the number becomes `1` and every time we do so, we we check that remainder after division is always `0`. Otherwise, the number can't be a power of two.
check that remainder after division is always `0`. Otherwise, the number can't be a power of two.
**Bitwise solution** **[Bitwise solution](https://github.com/Rudxain/javascript-algorithms/blob/master/src/algorithms/math/bits/isPowerOfTwo.js)**
Powers of two in binary form always have just one bit set.
The only exception is with a signed integer (e.g. an 8-bit
signed integer with a value of -128 looks like: `10000000`)
```
1: 0001
2: 0010
4: 0100
8: 1000
```
So after checking that the number is greater than zero,
we can use a bitwise hack to test that one and only one
bit is set.
```
number & (number - 1)
```
For example for number `8` that operations will look like:
```
1000
- 0001
----
0111
1000
& 0111
----
0000
```
## References ## References
- [GeeksForGeeks](https://www.geeksforgeeks.org/program-to-find-whether-a-no-is-power-of-two/) - [GeeksForGeeks](https://www.geeksforgeeks.org/program-to-find-whether-a-no-is-power-of-two/)
- [Bitwise Solution on Stanford](http://www.graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2)
- [Binary number subtraction on YouTube](https://www.youtube.com/watch?v=S9LJknZTyos&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=66) - [Binary number subtraction on YouTube](https://www.youtube.com/watch?v=S9LJknZTyos&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=66)

View File

@ -1,23 +1,27 @@
import isPowerOfTwo from '../isPowerOfTwo'; import f from '../isPowerOfTwo';
describe('isPowerOfTwo', () => { describe('isPowerOfTwo', () => {
it('should check if the number is made by multiplying twos', () => { it('should check if the number is made by multiplying twos', () => {
expect(isPowerOfTwo(-1)).toBe(false); // test all pows until it gets as close as possible to `Number.MAX_VALUE`
expect(isPowerOfTwo(0)).toBe(false); for (let i = 0; i <= 0x3ff; i++) {
expect(isPowerOfTwo(1)).toBe(true); expect( f(2 ** i) && f(1n << BigInt(i)) ).toBe(true)
expect(isPowerOfTwo(2)).toBe(true); }
expect(isPowerOfTwo(3)).toBe(false); expect(f(-8.5)).toBe(false);
expect(isPowerOfTwo(4)).toBe(true); expect(f(-1) || f(-1n)).toBe(false);
expect(isPowerOfTwo(5)).toBe(false); expect(f(0) || f(0n)).toBe(false);
expect(isPowerOfTwo(6)).toBe(false); expect(f(0.5)).toBe(false);
expect(isPowerOfTwo(7)).toBe(false); expect(f(Math.E)).toBe(false);
expect(isPowerOfTwo(8)).toBe(true); expect(f(3) || f(3n)).toBe(false);
expect(isPowerOfTwo(10)).toBe(false); expect(f(Math.PI)).toBe(false);
expect(isPowerOfTwo(12)).toBe(false); expect(f(5) || f(5n)).toBe(false);
expect(isPowerOfTwo(16)).toBe(true); expect(f(6) || f(6n)).toBe(false);
expect(isPowerOfTwo(31)).toBe(false); expect(f(7) || f(7n)).toBe(false);
expect(isPowerOfTwo(64)).toBe(true); expect(f(10) || f(10n)).toBe(false);
expect(isPowerOfTwo(1024)).toBe(true); expect(f(12) || f(12n)).toBe(false);
expect(isPowerOfTwo(1023)).toBe(false); expect(f(31) || f(31n)).toBe(false);
expect(f(1023) || f(1023n)).toBe(false);
expect( f(2 ** 32 - 1) || f(~(-1n << 32n)) ).toBe(false);
expect(f(Infinity) || f(-Infinity)).toBe(false);
expect(f(NaN)).toBe(false);
}); });
}); });

View File

@ -1,23 +0,0 @@
import isPowerOfTwoBitwise from '../isPowerOfTwoBitwise';
describe('isPowerOfTwoBitwise', () => {
it('should check if the number is made by multiplying twos', () => {
expect(isPowerOfTwoBitwise(-1)).toBe(false);
expect(isPowerOfTwoBitwise(0)).toBe(false);
expect(isPowerOfTwoBitwise(1)).toBe(true);
expect(isPowerOfTwoBitwise(2)).toBe(true);
expect(isPowerOfTwoBitwise(3)).toBe(false);
expect(isPowerOfTwoBitwise(4)).toBe(true);
expect(isPowerOfTwoBitwise(5)).toBe(false);
expect(isPowerOfTwoBitwise(6)).toBe(false);
expect(isPowerOfTwoBitwise(7)).toBe(false);
expect(isPowerOfTwoBitwise(8)).toBe(true);
expect(isPowerOfTwoBitwise(10)).toBe(false);
expect(isPowerOfTwoBitwise(12)).toBe(false);
expect(isPowerOfTwoBitwise(16)).toBe(true);
expect(isPowerOfTwoBitwise(31)).toBe(false);
expect(isPowerOfTwoBitwise(64)).toBe(true);
expect(isPowerOfTwoBitwise(1024)).toBe(true);
expect(isPowerOfTwoBitwise(1023)).toBe(false);
});
});

View File

@ -1,24 +1,37 @@
/** /**
* @param {number} number * @param {number|bigint} numeric value to check
* @return {boolean} * @return {boolean}
*/ */
export default function isPowerOfTwo(number) { export default function isPowerOfTwo(x) {
// 1 (2^0) is the smallest power of two. // 1 (2^0) is the smallest power of two.
if (number < 1) { if (x <= 1) {
return false; return x == 1;
}
// guard clause to dispatch BigInts ASAP
if (typeof x == "bigint") {
// the bitwise alt works for any BigInt
// because bit operators are arbitrary-precision for them
return (x & (x - 1n)) === 0n;
} }
// Let's find out if we can divide the number by two // Let's find out if we can divide the number by two
// many times without remainder. // many times without remainder.
let dividedNumber = number; let dividedNum = x / 2;
while (dividedNumber !== 1) { while (dividedNum !== 1) {
if (dividedNumber % 2 !== 0) { if ( !Number.isInteger(dividedNum) ) {
// For every case when remainder isn't zero we can say that this number /*
// couldn't be a result of power of two. For every case when quotient is non-int we can say that this number
couldn't be a result of power of two (with integer exponent),
because checking the fractional part of a quotient is equivalent to checking if the remainder is 0,
and the remainder is the value of the least significant bit (with fractional part included).
All powers of 2 (but `1`) must have consecutive trailing zeros.
this also immediately dispatches non-int values of `x`, because neither rem nor quotient is an int
*/
return false; return false;
} }
dividedNumber /= 2; dividedNum /= 2;
} }
return true; return true;

View File

@ -1,26 +0,0 @@
/**
* @param {number} number
* @return {boolean}
*/
export default function isPowerOfTwoBitwise(number) {
// 1 (2^0) is the smallest power of two.
if (number < 1) {
return false;
}
/*
* Powers of two in binary look like this:
* 1: 0001
* 2: 0010
* 4: 0100
* 8: 1000
*
* Note that there is always exactly 1 bit set. The only exception is with a signed integer.
* e.g. An 8-bit signed integer with a value of -128 looks like:
* 10000000
*
* So after checking that the number is greater than zero, we can use a clever little bit
* hack to test that one and only one bit is set.
*/
return (number & (number - 1)) === 0;
}