Работа с десятичными знаками в Ruby on Rails 3 - PullRequest
11 голосов
/ 19 января 2011

Я пытаюсь рассчитать среднюю чистую цену товара. В моей модели продукта есть: total_sold и: total_net_revenue. Прямое деление в методе, кажется, всегда приводит к 0. Я прибег к использованию BigDecimal, как я полагал, что это было проблемой ... но с моей последней итерацией кода ниже, я все равно получаю ноль, когда ответ приходит к десятичное число.

def avg_price
  BigDecimal(total_sold.to_s) / (BigDecimal(total_net_revenue.to_s) / 100)
end  

Чистая выручка в центах, поэтому я делю на 100. Кто-то может указать, что я делаю неправильно или должен делать?

Ответы [ 5 ]

13 голосов
/ 19 января 2011
total_net_revenue / total_sold

или

total_net_revenue / total_sold / 100.0

или

total_net_revenue.to_f / total_sold / 100

Эти три метода увеличивают точность, если хотите. Помните, что "средняя цена" - это "средняя цена / продажа. Это деньги за единицу, поэтому вам нужно будет выполнить деление в этом конкретном порядке.

12 голосов
/ 19 января 2011

Первое: вы делите неправильный путь.

100 штук / 150 долл. США = .667 штук за доллар

против

150 долл. США / 100 наименований = 1,50 долл. США за единицу

Во-вторых: Как и в других языках, вам нужно сделать так, чтобы одно из чисел в уравнении было десятичным, чтобы результат также приводился как один. Поскольку ваш доход представляет собой целое число, это означает, что все три значения были целыми числами, что означает, что в результате вы получили целое число. Чтобы получить десятичное число, приведите один из них к числу с плавающей запятой.

Другими словами, чтобы получить то, что вам нужно, сделайте это:

price_per_item = (total_net_revenue.to_f / 100) / total_sold
8 голосов
/ 19 января 2011

То, что вы делаете, называется целочисленным делением, которое отбрасывает любой остаток, так как оно не может быть выражено в целых числах, например:

1 / 3 # == 0

Как уже упоминали другие респонденты, вы можете вызвать плавающее числоточка деления.Вы должны заставить первый аргумент быть float (1 здесь), вызывая .to_f.Второй аргумент будет автоматически преобразован в число с плавающей точкой, например:

1.to_f / 3 # ~ 0.3333...

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

Точные детали более сложны.Я полагаю, что в двоичной арифметике с плавающей запятой, которая является обычной в современных микропроцессорах, степени 2 все еще точны.Но целое число 3, например, больше не представляется точно, а только в пределах точности представления с плавающей запятой (обычно 1E-16 или около того для «двойной» точности).

Короче говоря, здесьпрактическое правило: если вы имеете дело с денежными значениями, где точность имеет значение (когда-либо замечалось расхождение в 1 цент в телефонном счете?), не храните вычисленные результаты и не храните значения в плавающих точках.Вместо этого используйте целочисленные или десятичные типы данных (которые внутренне хранят строки).Вычислять результаты с плавающей запятой только для отображения и по запросу, если это возможно.Избегайте добавления больших и малых значений вместе, как только они являются плавающими, и избегайте цепных вычислений в плавающих.Переработайте свою алгебру, чтобы избежать делений до конца.Ruby также поддерживает тип данных Rational, который точно представляет дроби и может быть полезен.

Эти проблемы относятся к науке о «распространении ошибок с плавающей запятой», в которой вы можете найти дополнительную информацию, если вам нужно.

3 голосов
/ 19 января 2011

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

Так что в целом это работаеткак это:

some_integer.to_f / some_other_integer.to_f  # returns a float
0 голосов
/ 18 апреля 2012

Даже если одним из значений является Float, результатом будет Float.

Также, если вы используете Rails и ваши хранилища данных в конкретной модели, ActiveRecord имеет специальные методы, и вам не нужно рассчитывать самостоятельно.

Model.average("field_with_data")

Также доступны методы минимума, максимума, счета, суммы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...