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

5.3. Округление чисел с плавающей точкой

5.3. Округление чисел с плавающей точкой

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

Спок: Трудно сказать точно, капитан. Приблизительно 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. На этот случай следовало бы предусмотреть проверку ошибок.

Оглавление книги

Оглавление статьи/книги

Генерация: 1.046. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз