Треки
/
Julia
Julia
/
Вправи
/
Секрети
Секрети

Секрети

Навчальна вправа

Вступ

Двійкові цифри зрештою безпосередньо відповідають транзисторам у центральному процесорі (CPU) чи оперативній памʼяті (RAM), а також тому, чи кожен із них «увімкнений» чи «вимкнений».

Низькорівневе маніпулювання бітами, яке неформально називають «bit-twiddling», особливо важливе в системних мовах.

Високорівневі мови, як-от Julia, зазвичай абстрагують більшість цих деталей. Однак повний набір операцій на рівні бітів доступний уже в самій мові.

Примітка: Щоб побачити в REPL двійковий вивід у зручному для читання вигляді, майже всі наведені нижче приклади треба обгорнути у функцію bitstring(). Це відволікає візуально, тож із більшості прикладів цю функцію прибрано.

Операції побітового зсуву

Типи цілих чисел, знакові чи беззнакові, можна подати як рядок із 1 і 0.

julia> bitstring(UInt8(5))
"00000101"

Побітові зсуви просто пересувають усе ліворуч або праворуч на вказану кількість позицій. У типах UInt частина бітів зникає з одного кінця, а інший кінець доповнюється нулями:

julia> ux::UInt8 = 5
5

julia> bitstring(ux)
"00000101"

julia> ux << 2 # left by 2
"00010100"

julia> ux >> 1 # right by 1
"00000010"

Кожен зсув ліворуч подвоює значення, а кожен зсув праворуч ділить його навпіл (з урахуванням усічення). Це помітніше в десятковому поданні:

julia> 3 << 2
12

julia> 24 >> 3
3

Такі побітові зсуви працюють значно швидше за «справжню» арифметику, що робить цей прийом дуже популярним у низькорівневому програмуванні.

Із знаковими цілими числами треба бути трохи обережнішими.

Зсуви ліворуч відносно прості:

julia> sx = Int8(5)
5

julia> sx # positive integer
"00000101"

julia> sx << 2
"00010100"

julia> -sx # negative integer
"11111011"

julia> -sx << 2
"11101100"

Отже, зсув ліворуч для додатних знакових цілих чисел такий самий, як для беззнакових.

Відʼємні значення зберігаються у формі доповняльного коду, тобто найлівіший біт у них дорівнює 1. Із зсувом ліворуч жодних проблем, але як під час зсуву праворуч доповнювати найлівіші біти?

julia> sx >> 2 # simple for positive values!
"00000001"

julia> -sx # negative integer
"11111011"

julia> -sx >> 2 # pad with repeated sign bit
"11111110"

julia> -sx >>> 2 # pad with 0
"00111110"

Оператор >> виконує арифметичний зсув, зберігаючи знаковий біт.

Оператор >>> виконує логічний зсув, доповнюючи нулями, ніби число було беззнаковим.

Побітова логіка

Раніше, у попередньому концепті, ми бачили, що оператори && (логічне і), || (логічне або) та ! (заперечення) застосовують до булевих значень (англ. Boolean).

Існують відповідні оператори & (порозрядне і), | (порозрядне або) і ~ (тильда, порозрядне не), щоб порівнювати біти двох цілих чисел.

julia> 0b1011 & 0b0010 # bit is 1 in both numbers
"00000010"

julia> 0b1011 | 0b0010 # bit is 1 in at least one number
"00001011"

julia> ~0b1011 # flip all bits
"11110100"

julia> xor(0b1011, 0b0010) # bit is 1 in exactly one number, not both
"00001001"

Тут xor() означає виключне або і застосовується як функція.

Вказівки

Наш друг щойно надіслав нам повідомлення з важливою таємницею. Щоб інші не могли легко його прочитати, повідомлення зашифрували, виконавши низку операцій над бітами. Нам потрібно буде написати функції, які допоможуть розшифрувати повідомлення.

1. Зсунути біти назад

Перший крок до розшифрування повідомлення - скасувати зсув, зроблений під час шифрування, зсунувши біти назад праворуч. На подальших кроках розшифрування вважається, що нулі вставляються з лівого боку.

Реалізуйте функцію shift_back(), яка приймає значення та кількість позицій для зсуву й виконує зсув.

shift_back(0b1001, 2) # => 0b0010

2. Встановити деякі біти

Далі, деякі біти потрібно встановити в 1.

Реалізуйте функцію set_bits(), яка приймає значення та маску й повертає результат встановлення бітів у значенні в 1. Біт зі value потрібно встановити в 1 там, де біт у mask також дорівнює 1. Усі інші біти слід залишити без змін.

set_bits(0b0110, 0b0101) # => 0b0111

3. Перевернути певні біти

Під час шифрування деякі біти перевертаються. Щоб розшифрувати повідомлення, їх потрібно перевернути назад.

Реалізуйте функцію flip_bits(), яка приймає значення та маску. Маска вказує, які біти у значенні перевертати. Якщо біт у mask дорівнює 1, відповідний біт у value перевертається. Усі інші біти залишаються без змін.

flip_bits(0b1100, 0b0101) # => 0b1001

4. Очистити певні біти

Нарешті, є також певні біти, які завжди розшифровуються в 0.

Реалізуйте функцію clear_bits(), яка приймає значення та маску. Біти у value потрібно встановити в 0 там, де біт у масці дорівнює 1. Усі інші біти слід залишити без змін.

clear_bits(0b0110, 0b0101) # => 0b0010
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Julia Exercism

Час розпочати Секрети?

Зареєструйтеся на Exercism, щоб вивчати й опановувати Julia, а також 35 концепцій128 вправ та справжнє наставництво від людей, і все це безкоштовно.