Некоторые вспомогательные функции для работы с битами

В этом скромном рецепте мы предлагаем вам некоторые функции, которые потенциально могут облегчить вам программирование, если вы вдруг будете работать со значениями как с наборами битов. К сожалению, в рецепте практически нет ничего оригинального, и большая часть реализации взята из одной очень интересной библиотеки для Python 3, но это не значит, что в изложенном нами нет полезных решений…

Итак, если вы вдруг нуждаетесь в какой-то процедуре работы с побитовыми операциями, то любезно предлагаем вам ознакомиться с тем, что мы собрали в данной скромной статье.

Без лишних слов, предлагаем вам код обобщенных функций (с комментариями) для некоторых операций с битами:

// дополнение до 2
auto twoComplement(T)(T n, T bits)
{
	return ((cast(T) 1 << bits) - n) % (cast(T) 1 << bits);
}

// дополнение до 1
auto oneComplement(T)(T n, T bits)
{
	return (cast(T) 1 << bits) - n - cast(T) 1;
}

// подсчет количества установленных в единицу бит
auto countBits(T)(T n)
{
	T count;
	if (n == 0)
	{
		count++;
	}
	else
	{
		while (n != 0)
		{
			n >>= 1;
			count++;
		}
	}
	return count;
}

// установить нужный бит в единицу
auto setBit(T)(T n, T i)
{
	return n | (cast(T) 1 << i);
}

// получить значение нужного бита
auto getBit(T)(T n, T i)
{
	return (n >> i) & cast(T) 1;
}

// переключить нужный бит
auto toggleBit(T)(T n, T i)
{
	return n ^ (cast(T) 1 << i);
}

// сбросить необходимый бит
auto unsetBit(T)(T n, T i)
{
	return (n | (cast(T) 1 << i)) ^ (cast(T) 1 << i);
}

// создать битовую маску (n - количество единиц, k - количество последующих за ними нулей)
auto createMask(T)(T n, T k)
{
	return ((cast(T) 1 << n) - cast(T) 1) << k;
}

// извлечь биты (смысл k и n как и в предыдущем случае)
auto extractBitField(T)(T x, T n, T k)
{
	return (x & createMask(n, k)) >> k;
}

// номер последнего установленного бита
auto lastSettedBit(T)(T n)
{
	return countBits(n) - cast(T) 1;
}

// номер последнего сброшенного в ноль бита
auto lastUnsettedBit(T)(T n)
{
	return lastSettedBit(
		oneComplement(
			n, 
			countBits(n)
		)
	);
}

// количество нулевых бит в конце числа)
auto countTrailingZeroes(T)(T n)
{
	if (n == 0)
	{
		return 0;
	}
	else
	{
		return countBits(n & (-n)) - cast(T) 1;
	}
}

// удалить нули в конце числа
auto removeTrailingZeroes(T)(T n)
{
	if (n == 0)
	{
		return 0;
	}
	else
	{
		return n >> countTrailingZeroes(n);
	}
}

// догарифм по основанию два с округлением в большую сторону
auto ceilLog2(T)(T n)
{
	T x = lastSettedBit(n);
	return x + cast(T) (n != (cast(T) 1 << x));
}

// логарифм по основанию два с округлением в меньшую сторону
auto floorLog2(T)(T n)
{
	return countBits(n) - cast(T) 1;
}

// следующая степень двойки близкая к заданному числу 
auto nextPowerOfTwo(T)(T n)
{
	return cast(T) 1 << ceilLog2(n);
}

// выравнивание двух значений
auto alignValues(T, S)(T a, S b)
{
	auto lengthA = countBits(a);
	auto lengthB = countBits(b);
	
	if (lengthA > lengthB)
	{
		b <<= (lengthA - lengthB);
	}
	else
	{
		if (lengthA < lengthB)
		{
			a <<= lengthB - lengthA;
		}
	}
	
	auto trailingA = countTrailingZeroes(a);
	auto trailingB = countTrailingZeroes(b);
	
	a >>= min(trailingA, trailingB);
	b >>= min(trailingA, trailingB);
	
	return tuple(a, b);
}

Примечание автора. Если вдруг вам доведется применять эти операции с целыми числовыми типами в D (данные функции не работают с дробными типами, но при этом срабатывают на символьных, к примеру на char), то внимательно отслеживайте проделываемые операции. Дело в том, что иногда операции сдвига (и подобные им) делают так, что число «съезжает», т.е. сдвиговая операция предполагает расширение типа на значение большего размера (размер, разумеется в битах) — и вот тогда данный рецепт вам ничем не сможет помочь… К примеру, в практике одного из авторов блога, при использовании типа ulong произошел как раз такой случай: результат сдвига просто влево просто не помещается в ulong, и автора выручил несколько иной тип данных — BigInt из std.bigint (к сожалению, беззнаковый сдвиг вправо не поддерживается), но это было очень неудачное решение проблемы…

P.S: Мы не упоминаем наименование библиотеки на Python3 и не даем на нее ссылку, поскольку, это нарушит интригу одной из последующих статей. Обещаем, что в этой (одной из следующих) статье мы обязательно укажем ссылку на библиотеку и упомянем ее название.

Добавить комментарий