Конечно, если сдвиг настолько велик, что дает нулевое значение, то знаковый бит теряется, поскольку -0 и 0 — одно и то же.
Квадратные скобки позволяют трактовать числа как битовые массивы. Бит с номером 0 всегда является младшим, вне зависимости от порядка битов в конкретной машинной архитектуре.
x = 5 # То же, что 0b0101
а = x[0] # 1
b = x[1] # 0
с = x[2] # 1
d = x[3] # 0
# И так далее # 0
Присваивать новые значения отдельным битам с помощью такой нотации невозможно (поскольку Fixnum
хранится как непосредственное значение, а не как ссылка на объект). Но можно имитировать это действие путем сдвига 1 влево на нужное число позиций с последующим выполнением операции ИЛИ или И.
# Выполнить присваивание x[3] = 1 нельзя,
# но можно поступить так:
x |= (1<<3)
# Выполнить присваивание x[4] = 0 нельзя,
# но можно поступить так:
x &= ~(1<<4)
5.18. Преобразование системы счисления
Ясно, что любое целое число можно представить в любой системе счисления, поскольку хранятся эти числа в двоичном виде. Мы знаем, что Ruby умеет работать c целыми константами, записанными в любой из четырех наиболее популярных систем. Следовательно, разговор о преобразовании системы счисления может вестись только применительно к числам, записанным в виде строк.
Вопрос о преобразовании строки в целое рассмотрен в разделе 2.24. Для преобразования числа в строку проще всего воспользоваться методом to_s
, которому можно еще передать основание системы счисления. По умолчанию оно равно 10, но в принципе может быть любым вплоть до 36 (когда задействованы все буквы латинского алфавита).
237.to_s(2) # '11101101'
237.to_s(5) # '1422'
237.to_s(8) # '355'
237.to_s # '237'
237.to_s(16) # 'ed'
237.to_s(30) # '7r'
Другой способ — обратиться к методу %
класса String
:
hex = '%x' % 1234 # '4d2'
oct = '%о' % 1234 # '2322'
bin = '%b' % 1234 # '10011010010'
Метод sprintf
тоже годится:
str = sprintf(str,'Nietzsche is %x
',57005)
# str теперь равно: 'Nietzsche is dead
'
Если нужно сразу же вывести преобразованное в строку значение, то подойдет и метод printf
.
5.19. Извлечение кубических корней, корней четвертой степени и т.д.
В Ruby встроена функция извлечения квадратного корня (Math.sqrt
), поскольку именно она применяется чаще всего. А если надо извлечь корень более высокой степени? Если вы еще не забыли математику, то эта задача не вызовет затруднений.
Можно, например, воспользоваться логарифмами. Напомним, что е в степени x — обратная функция к натуральному логарифму x и что умножение чисел эквивалентно сложению их логарифмов.
x = 531441
cuberoot = Math.exp(Math.log(x)/3.0) # 81.0
fourthroot = Math.exp(Math.log(x)/4.0) # 27.0
Но можно просто использовать дробные показатели степени (оператор возведения в степень принимает в качестве аргумента произвольное целое число или число с плавающей точкой).
include Math
y = 4096
cuberoot = y**(1.0/3.0) # 16.0
fourthroot = y**(1.0/4.0) # 8.0
fourthroot = sqrt(sqrt(y)) # 8.0 (то же самое)
twelfthroot = y**(1.0/12.0) # 2.0
Отметим, что во всех примерах мы пользовались при делении числами с плавающей точкой (чтобы избежать отбрасывания дробной части).
5.20. Определение порядка байтов
Интересно, что производители компьютеров никак не могут договориться, в каком порядке лучше хранить двоичные байты. Следует ли размещать старший бит по большему или по меньшему адресу? При передаче сообщения по проводам нужно сначала посылать старший или младший бит?
Хотите верьте, хотите нет, но решение не произвольно. Существуют убедительные аргументы в пользу обеих точек зрения (обсуждать их здесь мы не будем).
Вот уже больше двадцати лет, как для описания противоположных позиций применяются термины «остроконечный» (little-endian) и «тупоконечный» (big-endian). Кажется, впервые их употребил Дэнни Коэн (Danny Cohen); см. его классическую статью 'On Holy Wars and a Plea for Peace' (IEEE Computer, October 1981). Взяты они из романа Джонатана Свифта «Путешествия Гулливера».
Обычно нам безразличен порядок байтов в конкретной машинной архитектуре. Но как быть, если все-таки его нужно знать?
Можно воспользоваться показанным ниже методом. Он возвращает одну из строк LITTLE
, BIG
или OTHER
. Решение основано на том факте, что директива l
выполняет упаковку в машинном формате, а директива N
распаковывает в сетевом порядке байтов (по определению тупоконечном).
def endianness
num = 0x12345678
little = '78563412'
big = '12345678'
native = [num].pack('1')
netunpack = native.unpack('N')[0]
str = '%8x' % netunpack
case str