Ruby/Подробнее о числах
Подробнее о числах
правитьИзначально числа представлены тремя типами: два целых типа (классы Fixnum
и Bignum
) и один с плавающей запятой (класс Float
). Возможно подключение дополнительных типов, например, комплексных и рациональных чисел, но пока ограничимся тремя.
Целые числа
правитьЦелые числа в Ruby не ограничены по величине, то есть могут хранить сколь угодно большие значения. Для обеспечения такого волшебного свойства было создано два класса. Один из них хранит числа меньше (по модулю), а второй — всё, что больше. По сути, для больших чисел создаётся массив из маленьких, а раз массив не имеет ограничений по длине, то и число получается неограниченным по значению.
Как ни странно, определяется как (2**30).class #=> Bignum
Однако, целое число, меньшее (по модулю) определяется как ((2**30)-1).class #=> Fixnum
(-(2**30)+1).class #=> Fixnum
Так-то! |
Как только число типа Fixnum
становится больше или равным (по модулю), то оно преобразуется к классу Bignum
. Если число типа Bignum
становится меньше , то оно преобразуется к типу Fixnum
.
При записи целых чисел сначала указывается знак числа (знак +
обычно не пишется). Далее идёт основание системы счисления, в которой задаётся число (если оно отлично от десятичной): 0
— для восьмеричной, 0x
— для шестнадцатеричной, 0b
— для двоичной. Затем идёт последовательность цифр, выражающих число в данной системе счисления. При записи чисел можно использовать символ подчёркивания, который игнорируется при обработке. Чтобы закрепить вышесказанное, посмотрим примеры целых чисел:
# тип Fixnum
123_456 # подчёркивание игнорируется
-567 # отрицательное число
0xbad # шестнадцатеричное число
0377 # восьмеричное
-0b101010 # отрицательное двоичное
0b0101_0101 # подчёркивание игнорируется
# тип Bignum
123_456_789_123_456 # подчёркивание игнорируется
-123_456_789_123_456 # отрицательное
07777777777777777777 # восьмеричное большое
Как видно из примеров, маленькие целые (Fixnum
) и больши́е целые (Bignum
) отличаются только значением.
Числа с плавающей запятой
правитьЧисла с плавающей запятой задаются только в десятичной системе счисления, при этом для отделения дробной части используется символ .
(точка). Для задания чисел с плавающей запятой может быть применена и экспоненциальная форма записи: два различных представления 0.1234e2
и 1234e-2
задают одно и то же число 12.34
.
# тип Float
-12.34 # отрицательное число с плавающей запятой
0.1234е2 # экспоненциальная форма для числа 12.34
1234е-2 # экспоненциальная форма для числа 12.34
Следует упомянуть, что чи́сла с плавающей запятой имеют фиксированный диапазон значений в отличие от целых чисел. Этот недостаток легко устраняется подключением библиотеки mathn
(подключаются рациональные и комплексные числа).
Семейный портрет чисел
правитьВ отличие от большинства элементарных типов данных, числа обладают своей иерархией. Все числа в Ruby наследованы от класса Numeric
(числовой). Поэтому, если хотите добавить новый метод ко всем числам, то нужно расширять именно этот класс. Далее идёт деление чисел: Integer
(целое), Float
(число с плавающей запятой) и Complex
(комплексное). При желании можно добавить и Rational
(рациональное), но на данном семейном портрете оно отсутствует.
От класса Integer
наследуются два класса: Fixnum
(фиксированное целое) и Bignum
(большое целое). К первому относятся все числа, по модулю меньшие , а ко второму — все остальные.
Fixnum
автоматически становитсяBignum
по превышении по модулю. И наоборот, падая ниже,Bignum
преобразуется вFixnum
.- Из отрицательного числа можно получить корень, когда подключена библиотека
mathn
. Он будет типаComplex
. - Как только число типа
Complex
лишается мнимой части, то оно становится либоInteger
(Fixnum
илиBignum
), либоFloat
(в зависимости от типа действительной части). Если подключена библиотекаmathn
, может получиться число типаRational
. - Если в результате арифметических действий в числе типа
Rational
знаменатель приравнивается1
, то оно преобразуется к числуInteger
.
Арифметические операции
правитьАрифметические операции в Ruby обычны: сложение (+
), вычитание (-
), умножение (*
), деление (/
), получение остатка от деления (%
), возведение в степень (**
).
6 + 4 #=> 10
6 - 4 #=> 2
6 * 4 #=> 24
6 / 4 #=> 1
6 % 4 #=> 2
6 ** 4 #=> 1296
Эти операции используются как числами с плавающей запятой, так и целыми числами (а также рациональными дробями и комплексными).
Порядок вычисления обычный. Для изменения приоритета применяются круглые скобки:
2 + 2 * 2 #=> 6
(2 + 2) * 2 #=> 8
Первое, что бросается в глаза, — результат арифметической операции двух целых чисел всегда будет целым. Особенно это видно при делении:
1/3 #=> 0
2/3 #=> 0
3/3 #=> 1
Если все аргументы арифметического выражения целые числа, то результат будет целым, если хотя бы одно число с плавающей запятой, то результат будет числом с плавающей запятой.
Одна вторая в Ruby ноль,
А три вторые — единица.
Запомнить надо эту соль,
Чтоб результату не дивиться.
Посмотрим, каковы результаты, когда одно из чисел является числом с плавающей запятой.
6.0 + 4 #=> 10.0
6 - 4.0 #=> 2.0
6.0 * 4.0 #=> 24.0
6.0 / 4 #=> 1.5 (одно из чисел с плавающей запятой, значит результат с плавающей запятой)
6.0 % 4 #=> 2.0
6 ** 4.0 #=> 1296.0
Лучше проверить эти сведения самостоятельно.
Поразрядная арифметика
правитьЗнак операции | Название |
---|---|
&
|
Побитовое «и» |
|
|
Побитовое «или» |
^
|
Побитовое «исключающее или» |
<<
|
Побитовый сдвиг влево |
>>
|
Побитовый сдвиг вправо |
~
|
Побитовая инверсия |
Операции побитовой арифметики заимствованы из языка Си. На этот раз без всяких экзотических особенностей.
6 & 4 #=> 4
6 | 4 #=> 6
6 ^ 4 #=> 2
6 << 4 #=> 96
6 >> 4 #=> 0 (чересчур намного сдвинули)
~4 #=> -5 (операция только над одним аргументом)
Здесь, вроде, всё понятно и без дополнительных пояснений. А если непонятно, то справочник по языку Си поможет.
Операции с присваиванием
правитьЧасто можно встретить выражения вида:
number_one += number_two
Это выполнение операции сразу с присваиванием. Вышеуказанная запись равнозначна следующей:
number_one = number_one + number_two
Вполне естественно, что вместо операции +
может использоваться любая другая, а вместо чисел могут быть другие типы данных.
string = "едем"
string += ", "
string *= 3
string #=> "едем, едем, едем, "
array = [1, 2, 3]
array += [4, 5]
array #=> [1, 2, 3, 4, 5]
При определении метода +
метод +=
вы получаете в подарок. Это правило касается всех бинарных операций, обозначаемых значками.
Методы явного преобразования типов
правитьМетод | Операция |
---|---|
to_f |
Преобразовать в число с плавающей запятой |
to_i |
Преобразовать в целое число |
to_s |
Преобразовать в строку |
to_a |
Преобразовать в массив (до версии 1.9+) |
Методы преобразования типов в Ruby традиционно начинаются с приставки to_
. Последующая буква — это сокращение от названия класса, в который происходит преобразование (f
— Float
— число с плавающей запятой, i
— Integer
— целое, s
— String
— строка, a
— Array
— массив). Посмотрим их действие на примере:
7.to_f #=> 7.0
7.9.to_i #=> 7
7.to_s #=> "7"
"7".to_a #=> ["7"]
Случайное число
правитьЧасто требуется получить случайное число. Пример:
rand(100) #=> 86
rand #=> 0.599794231588021
В первом случае метод rand
возвращает целое число в диапазоне от 0
до 99
(на единицу меньше 100). Во втором случае метод rand
возвращает число с плавающей запятой в диапазоне от 0.0
до 1.0
включительно. Различие в результате обусловлено передаваемым параметром:
- если передаётся параметр (в данном случае
100
), то генерируется целое случайное число (в диапазоне0..N-1
, гдеN
— передаваемый аргумент); - если параметр отсутствует, то генерируется число с плавающей запятой в диапазоне от
0.0
до1.0
.
Есть способ предсказать весь ряд «случайных» чисел. Делается это при помощи метода srand
. Ему передаётся целое число (идентификатор «случайной» последовательности). После этого весь случайный ряд можно предугадать. Проведём опыт: берусь угадать массив, который будет создан следующей программой.
srand 123
Array.new(5){ rand(100) } #=> [66, 92, 98, 17, 83]
Если вы выполните данную программу у себя, то получите тот же самый массив. 123 — номер «случайной» последовательности. Измените его и массив изменится!
Если вызвать srand
без параметра или не вызывать его вообще, то номер «случайной» последовательности выбирается случайным образом.
Хитрости
правитьЗадача: выдать целое число в двоичной системе счисления.
start_number = 1234
puts sprintf("%b", start_number) # метод sprintf заимствован из Си
puts start_number.to_s(2) # современный метод — означает «по основанию»,
# аргументом может служить не только 8 и 16, но и 5, 30…
# На самом деле, основание не может превышать 36,
# что вполне объяснимо — 10 цифр и 26 букв латинского алфавита.
Поменять порядок цифр данного числа на обратный:
start_number = 1234
puts start_number.to_s.reverse # метод reverse переворачивает строку
Получить значение N-го двоичного разряда данного целого числа:
start_number, N = 1234, 5
puts start_number[N]
Поменять целочисленные значения двух переменных без использования третьей переменной:
number_one, number_two = 134, 234
number_one, number_two = number_two, number_one
Округлить число с плавающей запятой до двух разрядов:
float_integer = 3.1415926535
puts (float_integer * 100).to_i.to_f / 100
puts ((float_integer + 0.005) * 100).to_i / 100.0
puts sprintf("%.2f", float_integer).to_f # полуСишный способ =)
На самом деле во второй строке оставляются два знака после запятой, а остальные просто отбрасываются безо всяких округлений, в то время как в третьей строке действительно происходит округление до двух знаков после запятой. Это легко проверить попытавшись округлить до трёх знаков после запятой:
float_integer = 3.1415926535
puts (float_integer * 1000).to_i.to_f / 1000 #=>3.141
puts ((float_integer + 0.0005) * 1000).to_i / 1000.0 #=>3.142
puts sprintf("%.3f", float_integer).to_f #=>3.142
Но всё же лучше:
float_integer = 3.1415926535
float_integer.round 3 #=>3.142 (возможно, что round округляет только до целых)