Float vs Decimal в ActiveRecord


283

Іноді типи даних Activerecord мене бентежать. Помилка, часто. Одне з моїх вічних питань - для даного випадку,

Чи варто використовувати :decimalабо :float?

Я часто стикався з цим посиланням, ActiveRecord:: decimal vs: float? , але відповіді не досить чіткі, щоб я був певним:

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

Ось кілька прикладів випадків:

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

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

  • Які переваги / недоліки використання кожного?
  • Які б хороші правила, щоб знати, який тип використовувати?

Відповіді:


427

Я пам’ятаю, як мій професор CompSci сказав, що ніколи не використовувати плавці для валюти.

Причиною тому є те, як специфікація IEEE визначає поплавці у двійковому форматі. В основному, він зберігає знак, дріб і експонент, щоб представляти Поплавок. Це як наукове позначення бінарного (щось подібне +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

Тож якщо ви маєте справу з дрібними дробами, як, наприклад, складання інтересів, або, можливо, навіть геолокація, я б дуже рекомендував десятичний формат, оскільки в десятковому форматі 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 знаходиться в base-2, тоді як десятковий - у base-10? Що було б корисним для поплавця? Що робить ваш приклад та демонструє?
Джонатан Аллард

1
Ви не маєте на увазі, +1.43*2^10а не +1.43*10^2?
Камерон Мартін

47
Для майбутніх відвідувачів найкращим типом даних для валюти є ціле число, а не десяткові. Якщо точність поля - копійки, то поле буде цілим числом у копійках (а не десятком у доларах). Я працював у відділі інформаційних технологій банку, і ось так це робилося. Деякі поля були з більшою точністю (наприклад, соті копійки), але вони все ще були цілими числами.
adg

1
@adg правильно: bigdecimal - це також поганий вибір валюти.
Ерік Думініл

1
@adg ти маєш рацію. Я працював з деякими бухгалтерськими та фінансовими програмами протягом останніх декількох років, і ми зберігаємо всі наші валютні поля в цілих стовпцях. Набагато безпечніше для цих випадків.
Гільгерме Лагес Сантос

19

У Rails 3.2.18: десятковий перетворюється на: ціле число при використанні SQLServer, але він добре працює в SQLite. Перехід до: float вирішив це питання для нас.

Урок, який ви отримали, - "завжди використовуйте однорідні бази розробки та розгортання!"


3
Добре, що через 3 роки робити Рейки, я цілком погоджуюся.
Джонатан Аллард

3
"завжди використовуйте однорідні бази розробки та розгортання!"
zx1986

15

У Rails 4.1.0 я зіткнувся з проблемою збереження широти та довготи до бази даних MySql. Він не може зберегти велике число дробу з плаваючим типом даних. І я змінюю тип даних на десятковий і працює для мене.

  def змінити
    change_column: міст,: широта,: decimal,: точність => 15,: scale => 13
    change_column: міст,: довгота,: decimal,: точність => 15,: scale => 13
  кінець

Я зберігаю свої: широта і: довгота, як пливе в Postgres, і це працює чудово.
Скотт W

3
@Robikul: так, це добре, але надмірність. decimal(13,9) достатня для широти і довготи. @ScottW: Я не пригадую, але якщо Postgres використовує IEEE floats, він лише "добре працює", оскільки ви не зіткнулися з проблемами ... І все-таки. Це недостатній формат для широти та довготи. Зрештою, Йо буде мати помилки в найменш значущих цифрах.
Lonny Everyus

@LonnyEachus, що робить IEEE плаває недостатньою для lat / longs?
Олександр Сурафель

3
@AlexanderSuraphel Якщо ви використовуєте десяткову широту та довготу, поплавок IEEE чутливий до помилок у найменш значущих цифрах. Таким чином, ваша широта і довгота можуть мати точність, наприклад, 1 метр, але у вас можуть бути помилки в 100 і більше метрів. Це особливо актуально, якщо ви використовуєте їх у розрахунках.
Lonny Everyus
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.