Poprzednio nauczyliśmy się liczyć trailing 0s w liczbach dwójkowych, teraz odtworzymy funkcjonalność znaną z Math.clz32. Do dzieła.
Zastanówmy się, jaka jest największa wartość jednego 1 w int32:
let pow1 = Math.pow(2, 31);
console.log(pow1); //2147483648
To nie jest największa liczba. To tylko liczba, która z przodu ma jedynkę. Jeszcze ma 31 miejsc, gdzie może być jedynka lub 0. Zobaczmy to i owo:
let pow1 = Math.pow(2, 31);
console.log(pow1); //2147483648
console.log(Math.clz32(pow1)); //0
console.log(Math.clz32(pow1+1)); //0
console.log(Math.clz32(pow1+100)); //0
console.log(Math.clz32(pow1+1000)); //0
Czasami warto sobie pewne rzeczy zwizualizować. Zobaczmy w Pythonie:
print(1 << 31) #2147483648
print("{0:b}".format(1 << 31))
#10000000000000000000000000000000
print("{0:b}".format(8))
#1000
print("{0:b}".format(8 << 4))
#10000000
print("{0:b}".format(8 << 8))
#100000000000
print("{0:b}".format(8 << 20))
#100000000000000000000000
print((1 << 31) & (1 << 30)) #0
print((1 << 31) & (1 << 31)) #2147483648
Jeszcze jeden rzut oka, który może nas naprowadzić na rozwiązanie:
console.log((1 << 31) & (1 << 31)); //-2147483648
console.log((1 << 31) & (1 << 30)); //0
console.log((1 << 31) & (1 << 29)); //0
console.log((1 << 31) & (1 << 28)); //0
console.log((1 << 31) & (1 << 27)); //0
console.log((1 << 31) & (1 << 26)); //0
console.log((1 << 31) & (1 << 25)); //0
Ok, nasza funkcja:
function countLeadingZeros(num)
{
let total_bits = 32;
let zeros = 0;
while ((num & (1 << (total_bits - 1))) === 0)
{
num = (num << 1);
zeros++;
}
return zeros;
}
Nasze testy:
console.log(countLeadingZeros(1 << 31)); //0
console.log(countLeadingZeros(1 << 30)); //1
console.log(countLeadingZeros(1 << 29)); //2
console.log(countLeadingZeros(1 << 28)); //3
console.log(countLeadingZeros(1 << 27)); //4
console.log(countLeadingZeros(1 << 26)); //5
console.log(countLeadingZeros(1 << 25)); //6
console.log(Math.clz32(8), countLeadingZeros(8)); //28 28
console.log(Math.clz32(9), countLeadingZeros(9)); //28 28
console.log(Math.clz32(32), countLeadingZeros(32)); //26 26
console.log(Math.clz32(34), countLeadingZeros(34)); //26 26
Jak to działa? Weźmy 8 i 9 dla przykładu i naszą maskę, która jest niezmienna (2^31 albo 1 << 31):
print("{0:b}".format(1 << 31))
#10000000000000000000000000000000
print("{0:b}".format(8))
#1000
print("{0:b}".format(9))
#1001
Jest maska i jest liczba. Dopiszmy sobie zera samemu:
Maska - 10000000000000000000000000000000
8 - 00000000000000000000000000001000
9 - 00000000000000000000000000001001
No i teraz robimy & tak długo jak & daje 0. I nic z maska nie robimy, tylko liczbę przesuwamy o << 1 w lewo. I ilość takich przesunięć zliczamy. Bardzo proste, ale trzeba to sobie rozpisać, rozrysować, przerobić.