Float vs Decimal в ActiveRecord

Иногда типы данных Activerecord путают меня. Err, часто. Один из моих вечных вопросов - для данного случая

Должен ли я использовать :decimal или :float?

Я часто сталкивался с этой ссылкой, ActiveRecord:: decimal vs: float?, но ответы не совсем ясны, чтобы я мог быть уверен:

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

Вот несколько примеров:

  • Геолокация/широта/долгота: -45.756688, 120.5777777,...
  • Отношение/процент: 0.9, 1.25, 1.333, 1.4143,...

Я использовал :decimal в прошлом, но я обнаружил, что обращение к объектам BigDecimal в Ruby было излишне неудобным по сравнению с поплавком. Я также знаю, что могу использовать :integer для представления денег/центов, например, но он не подходит для других случаев, например, когда количества, в которых точность может меняться со временем.

  • Каковы преимущества/недостатки использования каждого?
  • Что было бы хорошими эмпирическими правилами, чтобы узнать, какой тип использовать?

Ответ 1

Я помню, как мой профессор CompSci сказал, что никогда не использовать float для валюты.

Причиной этого является то, что спецификация IEEE определяет float в двоичном формате. В основном, он хранит знак, дробь и показатель для представления Float. Это похоже на научную нотацию для двоичного кода (что-то вроде +1.43*10^2). Из-за этого невозможно точно хранить фракции и десятичные знаки в Float.

Вот почему есть десятичный формат. Если вы это сделаете:

irb:001:0> "%.47f" % (1.0/10)
=> "0.10000000000000000555111512312578270211815834045" # not "0.1"!

тогда как если вы просто сделаете

irb:002:0> (1.0/10).to_s
=> "0.1" # the interprer rounds the number for you

Итак, если вы имеете дело с небольшими фракциями, такими как усугубляющие интересы или даже геолокация, я бы очень рекомендовал формат Decimal, поскольку в десятичном формате 1.0/10 составляет ровно 0,1.

Однако следует отметить, что, несмотря на меньшую точность, поплавки обрабатываются быстрее. Здесь контрольный показатель:

require "benchmark" 
require "bigdecimal" 

d = BigDecimal.new(3) 
f = Float(3)

time_decimal = Benchmark.measure{ (1..10000000).each { |i| d * d } } 
time_float = Benchmark.measure{ (1..10000000).each { |i| f * f } }

puts time_decimal 
#=> 6.770960 seconds 
puts time_float 
#=> 0.988070 seconds

Ответ

Используйте float, когда вы не слишком заботитесь о точности. Например, некоторым научным симуляциям и расчетам требуется только до 3 или 4 значащих цифр. Это полезно в торговле с точностью для скорости. Поскольку они не нуждаются в точности так же сильно, как скорость, они будут использовать float.

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

Ответ 2

В Rails 3.2.18: decimal превращается в: integer при использовании SQLServer, но отлично работает в SQLite. Переключение на: float решило эту проблему для нас.

Изученный урок "всегда использует однородные базы разработки и развертывания!"

Ответ 3

В Rails 4.1.0 у меня возникла проблема с сохранением широты и долготы в базе данных MySql. Он не может сэкономить большое количество фракций с типом данных float. И я меняю тип данных на десятичные и работаю для меня.

  def change
    change_column :cities, :latitude, :decimal, :precision => 15, :scale => 13
    change_column :cities, :longitude, :decimal, :precision => 15, :scale => 13
  end