Кирк: Какие, вы говорите, у нас шансы выбраться отсюда?

Спок: Трудно сказать точно, капитан. Приблизительно 7824.7 к одному.

Стар Трек, «Миссия милосердия»

Метод round округляет число с плавающей точкой до целого:

pi = 3.14159

new_pi = pi.round  # 3

temp = -47.6

temp2 = temp.round # -48

Иногда бывает нужно округлить не до целого, а до заданного числа знаков после запятой. В таком случае можно воспользоваться функциями sprintf (которая умеет округлять) и eval:

pi = 3.1415926535

pi6 = eval(sprintf('%8.6f',pi)) # 3.141593

pi5 = eval(sprintf('%8.5f',pi)) # 3.14159

pi4 = eval(sprintf('%8.4f',pi)) # 3.1416

Это не слишком красиво. Поэтому инкапсулируем оба вызова функций в метод, который добавим в класс Float:

class Float

 def roundf(places)

  temp = self.to_s.length

  sprintf('%#{temp}.#{places}f',self).to_f

 end

end

Иногда требуется округлять до целого по-другому. Традиционное округление n+0.5 с избытком со временем приводит к небольшим ошибкам; ведь n+0.5 все-таки ближе к n+1, чем к n. Есть другое соглашение: округлять до ближайшего четного числа, если дробная часть равна 0.5. Для реализации такого правила можно было бы расширить класс Float, добавив в него метод round2:

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

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

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

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату