Чтение онлайн

на главную - закладки

Жанры

Программирование на языке Ruby
Шрифт:

class Float

 def round2

whole = self.floor

fraction = self — whole

if fraction == 0.5

if (whole % 2) == 0

whole

else

whole+1

end

else

self.round

end

 end

end

a = (33.4).round2 # 33

b = (33.5).round2 # 34

с = (33.6).round2 # 34

d = (34.4).round2 # 34

e = (34.5).round2 # 34

f = (34.6).round2 # 35

Видно, что

round2
отличается от
round
только в том случае, когда дробная часть в точности равна 0.5. Отметим, кстати, что число 0.5 можно точно представить в двоичном виде. Не так очевидно, что этот метод правильно работает и для отрицательных чисел (попробуйте!). Отметим еще, что скобки в данном случае необязательны и включены в запись только для удобства восприятия.

Ну а если мы хотим округлять до заданного числа знаков после запятой, но при этом использовать метод «округления до четного»? Тогда нужно добавить в класс

Float
также метод
roundf2
:

class Float

 # Определение round2 такое же, как и выше.

 def roundf2(places)

shift = 10**places

(self * shift).round2 / shift.to_f

 end

end

a = 6.125

b = 6.135

x = a.roundf2(a) #6.12

y = b.roundf2(b) #6.13

У методов

roundf
и
roundf2
есть ограничение: большое число с плавающей точкой может стать непредставимым при умножении на большую степень 10. На этот случай следовало бы предусмотреть проверку ошибок.

5.4. Сравнение чисел с плавающей точкой

Печально, но факт: в компьютере числа с плавающей точкой представляются неточно. В идеальном мире следующий код напечатал бы «да», но на всех машинах где мы его запускали, печатается «нет»:

x = 1000001.0/0.003

y = 0.003*x

if y == 1000001.0

 puts "да"

else

 puts "нет"

end

Объясняется это тем, что для хранения числа с плавающей точкой выделено конечное число битов, а с помощью любого, сколь угодно большого, но конечного числа битов нельзя представить периодическую десятичную дробь с бесконечным числом знаков после запятой.

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

Ниже показан простой способ выполнения сравнения с «поправкой», когда числа считаются равными, если отличаются не более чем на величину, задаваемую программистом:

class Float

 EPSILON = 1e-6 # 0.000001

 def == (x)

(self-x).abs < EPSILON

 end

end

x = 1000001.0/0.003

y = 0.003*x

if y == 1.0 # Пользуемся новым оператором ==.

 puts "да" # Теперь печатается "да".

else

 puts "нет"

end

В зависимости от ситуации может понадобиться задавать разные погрешности. Для этого определим в классе

Float
новый метод
equals?
. (При таком выборе имени мы избежим конфликта со стандартными методами
equal?
и
eql?
; последний, кстати, вообще не следует переопределять).

class Float

 EPSILON = 1e-6

 def equals?(x, tolerance=EPSILON)

(self-x).abs < tolerance

 end

end

flag1 = (3.1416).equals? Math::PI # false

flag2 = (3.1416).equals?(Math::PI, 0.001) # true

Можно также ввести совершенно новый оператор для приближенного сравнения, назвав его, например,

=~
.

Имейте в виду, что это нельзя назвать настоящим решением. При последовательных вычислениях погрешность накапливается. Если вам совершенно необходимы числа с плавающей точкой, смиритесь с неточностями (см. также разделы 5.8 и 5.9).

5.5. Форматирование чисел для вывода

Для вывода числа в заданном формате применяется метод

printf
из модуля Kernel. Он практически не отличается от одноименной функции в стандартной библиотеке С. Дополнительную информацию см. в документации по методу
printf
.

x = 345.6789

i = 123

printf("x = %6.2f\n", x) # x = 345.68

printf("x = %9.2e\n", x) # x = 3.457e+02

printf("i = %5d\n\ i) # i = 123

printf("i = %05d\n", i) # i = 00123

printf("i = %-5d\n\, i) # i = 123

Чтобы сохранить результат в строке, а не печатать его немедленно, воспользуйтесь методом

sprintf
. При следующем обращении возвращается строка:

Поделиться:
Популярные книги

Кодекс Охотника. Книга XXVII

Винокуров Юрий
27. Кодекс Охотника
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Кодекс Охотника. Книга XXVII

Черный дембель. Часть 5

Федин Андрей Анатольевич
5. Черный дембель
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Черный дембель. Часть 5

Кодекс Охотника. Книга ХХХ

Винокуров Юрий
30. Кодекс Охотника
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Кодекс Охотника. Книга ХХХ

Мечников. Клятва лекаря

Алмазов Игорь
2. Жизнь Лекаря с нуля
Фантастика:
альтернативная история
аниме
фэнтези
попаданцы
6.60
рейтинг книги
Мечников. Клятва лекаря

Гримуар темного лорда IV

Грехов Тимофей
4. Гримуар темного лорда
Фантастика:
фэнтези
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Гримуар темного лорда IV

Последний Паладин. Том 4

Саваровский Роман
4. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Последний Паладин. Том 4

Идеальный мир для Лекаря 16

Сапфир Олег
16. Лекарь
Фантастика:
боевая фантастика
юмористическая фантастика
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 16

Чехов

Гоблин (MeXXanik)
1. Адвокат Чехов
Фантастика:
фэнтези
боевая фантастика
альтернативная история
5.00
рейтинг книги
Чехов

Вперед в прошлое 12

Ратманов Денис
12. Вперед в прошлое
Фантастика:
попаданцы
5.00
рейтинг книги
Вперед в прошлое 12

Компас желаний

Кас Маркус
8. Артефактор
Фантастика:
городское фэнтези
аниме
фэнтези
5.00
рейтинг книги
Компас желаний

Держать удар

Иванов Дмитрий
11. Девяностые
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Держать удар

Точка Бифуркации X

Смит Дейлор
10. ТБ
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Точка Бифуркации X

Газлайтер. Том 20

Володин Григорий Григорьевич
20. История Телепата
Фантастика:
боевая фантастика
аниме
попаданцы
5.00
рейтинг книги
Газлайтер. Том 20

Дважды одаренный. Том VI

Тарс Элиан
6. Дважды одаренный
Фантастика:
аниме
альтернативная история
фэнтези
фантастика: прочее
5.00
рейтинг книги
Дважды одаренный. Том VI