Float vs Decimal w ActiveRecord

Czasami typy danych Activerecord mylą mnie. Często. Jednym z moich odwiecznych pytań jest, dla danego przypadku,

Powinienem użyć :decimal Czy :float?

Często natknąłem się na ten link, ActiveRecord:: decimal vs: float?, ale odpowiedzi nie są wystarczająco jasne, aby być pewnym:

Widziałem wiele wątków, w których ludzie zalecają, aby nigdy nie używać flat out float i Zawsze używaj dziesiętnych. Widziałem też propozycje niektóre ludzie, aby używać float tylko do zastosowań naukowych.

Oto kilka przykładowych przypadków:

  • Geolokalizacja / Szerokość / Długość geograficzna: -45.756688, 120.5777777, ...
  • stosunek / procent: 0.9, 1.25, 1.333, 1.4143, ...

Używałem :decimal w przeszłości, ale okazało się, że radzenie sobie z BigDecimal obiektami w Rubim było niepotrzebnie niewygodne w porównaniu do float. Wiem też, że mogę użyć :integer do reprezentowania pieniędzy / centów, na przykład, ale to nie pasuje do innych przypadków, na przykład, gdy ilości, w których precyzja może zmieniać się w czasie.

  • jakie są zalety / wady korzystania z każdego z nich?
  • Jakie byłyby dobre zasady, aby wiedzieć, jakiego typu użyć?
Author: user2262149, 2011-12-15

3 answers

Pamiętam, jak mój profesor CompSci mówił, żeby nigdy nie używać pływaków dla pieniędzy.

Powodem tego jest sposób, w jaki Specyfikacja IEEE definiuje pływaki w formacie binarnym. Zasadniczo przechowuje znak, ułamek i wykładnik, aby reprezentować Float. To jak naukowy zapis binarny (coś w rodzaju +1.43*10^2). Z tego powodu niemożliwe jest dokładne przechowywanie ułamków i dziesiętnych w Float.

Dlatego istnieje format dziesiętny. Jeśli to zrobisz:

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

If you just do

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

Więc jeśli masz do czynienia z małymi ułamkami, jak łączenie interesów, a może nawet Geolokalizacja, Gorąco polecam format dziesiętny, ponieważ w formacie dziesiętnym 1.0/10 jest dokładnie 0.1.

Należy jednak zauważyć, że pomimo mniejszej dokładności, pływaki są przetwarzane szybciej. Oto benchmark:

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

Odpowiedź

Użyj float, gdy nie zależy Ci zbytnio na precyzji. Przykładowo, niektóre symulacje naukowe i obliczenia wymagają tylko do 3 lub 4 znaczących cyfr. Jest to przydatne w wymianie dokładności na szybkość. Ponieważ nie potrzebują tyle precyzji, co prędkości, użyliby float.

Użyj decimal Jeśli masz do czynienia z liczbami, które muszą być precyzyjne i sumować się do poprawnej liczby (jak łączenie interesów i rzeczy związanych z pieniędzmi). Pamiętaj: jeśli potrzebujesz precyzji, zawsze powinieneś używać dziesiętnych.

 373
Author: Iuri G.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-22 20:58:25

W Rails 3.2.18,: decimal zamienia się w: integer podczas używania SQLServer, ale działa dobrze w SQLite. Przejście na :float rozwiązało ten problem.

Wyciągnięta lekcja brzmi: "Zawsze używaj jednorodnych baz danych rozwoju i wdrażania!"

 16
Author: ryan0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-22 16:38:17

W Rails 4.1.0 napotkałem problem z zapisaniem szerokości i długości geograficznej do bazy danych MySql. Nie można zapisać dużej liczby ułamków z typem danych float. I zmieniam typ danych na dziesiętny i pracuje dla mnie.

  def change
    change_column :cities, :latitude, :decimal, :precision => 15, :scale => 13
    change_column :cities, :longitude, :decimal, :precision => 15, :scale => 13
  end
 8
Author: Rokibul Hasan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-03 08:14:26