ActiveRecord의 부동 대 소수


283

때로는 Activerecord 데이터 형식이 혼란 스럽습니다. 자 주요 내 영원한 질문 중 하나는 주어진 경우에

:decimal또는 사용해야합니까 :float?

나는 종종이 링크, ActiveRecord :: decimal vs : float? 그러나 대답은 확실하지 않습니다.

사람들이 float을 사용하지 않고 항상 10 진수를 사용하도록 플랫 아웃을 권장하는 많은 스레드를 보았습니다. 또한 일부 사람들은 과학 응용 프로그램에만 float를 사용하라는 제안을 보았습니다.

다음은 몇 가지 사례입니다.

  • 위치 정보 / 위도 / 경도 : -45.756688, 120.5777777, ...
  • 비율 / 비율 : 0.9, 1.25, 1.333, 1.4143, ...

나는 :decimal과거에 사용했지만 BigDecimalRuby에서 객체를 다루는 것이 플로트에 비해 불필요하게 어색 하다는 것을 알았습니다 . :integer예를 들어 돈 / 센트를 나타내는 데 사용할 수도 있지만 시간이 지남에 따라 정밀도가 변할 수있는 다른 경우에는 적합하지 않습니다.

  • 각각을 사용할 때의 장점 / 단점은 무엇입니까?
  • 어떤 유형을 사용해야하는지 아는 좋은 경험 법은 무엇입니까?

답변:


427

CompSci 교수는 통화에 플로트를 사용하지 말라고 말합니다.

그 이유는 IEEE 사양 이 이진 형식으로 부동 소수점정의 하는 방법 때문입니다 . 기본적으로 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

따라서 복리 관심사 또는 지리적 위치와 같은 작은 분수를 처리하는 경우 십진 형식 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를 사용합니다.

정확한 숫자가 필요한 숫자를 다룰 경우 십진수를 사용하십시오 . 기억하십시오 : 정밀도가 필요한 경우 항상 10 진수를 사용해야합니다.


따라서 올바르게 이해하면 float는 밑이 2이지만 소수점은 밑이 10입니다. 플로트에 좋은 용도는 무엇입니까? 당신의 모범은 무엇을하고 증명합니까?
Jonathan Allard

1
+1.43*2^10오히려 의미 하지 +1.43*10^2않습니까?
Cameron Martin

47
향후 방문자의 경우 통화에 가장 적합한 데이터 유형은 10 진수가 아닌 정수입니다. 필드의 정밀도가 페니 인 경우 필드는 페니의 정수 (십진수가 아닌 달러)가됩니다. 나는 은행의 IT 부서에서 일했고 그것이 그렇게 된 방법입니다. 일부 필드는 정밀도가 높았지만 (예 : 100 분의 1 페니) 여전히 정수였습니다.
adg

1
@adg가 옳습니다 : bigdecimal은 통화에 대한 좋지 않은 선택입니다.
Eric Duminil

1
@adg 당신이 맞아요. 지난 몇 년 동안 일부 회계 및 재무 응용 프로그램을 사용해 왔으며 모든 통화 필드를 정수 열에 저장합니다. 이 경우 훨씬 안전합니다.
길 레르 메 Lages 산토스

19

Rails 3.2.18에서 : decimal은 SQLServer를 사용할 때 : integer로 바뀌지 만 SQLite에서는 잘 작동합니다. : float로 전환하면이 문제가 해결되었습니다.

배운 교훈은 "항상 균질 한 개발 및 배포 데이터베이스를 사용하는 것"입니다.


3
좋은 점은 3 년 후 Rails를 수행 한 후 진심으로 동의합니다.
Jonathan Allard

3
"항상 균질 한 개발 및 배포 데이터베이스를 사용하십시오!"
zx1986

15

Rails 4.1.0에서 위도 및 경도를 MySql 데이터베이스에 저장하는 데 문제가 있습니다. float 데이터 형식으로 큰 분수를 저장할 수 없습니다. 그리고 데이터 유형을 10 진수로 변경하고 나를 위해 일합니다.

  데프 변경
    change_column : 도시, : 위도, : 10 진수, : 정밀도 => 15, : 스케일 => 13
    change_column : 도시, : 경도, : 10 진수, : 정밀도 => 15, : 스케일 => 13
  종료

Postgres에서 내 : latitude 및 : longitude를 float로 저장하면 제대로 작동합니다.
Scott W

3
@Robikul : 네, 좋습니다.하지만 과잉입니다. decimal(13,9) 위도와 경도에 충분합니다. @ScottW : 기억 나지 않지만 Postgres가 IEEE float를 사용하는 경우 문제가 발생하지 않기 때문에 "잘 작동합니다". 위도와 경도에 대한 형식이 충분하지 않습니다. Yo는 최하위 자릿수에 오류가 있습니다.
Lonny Eachus 2016 년

@LonnyEachus 위도 / 경도에 IEEE 플로트가 부족한 이유는 무엇입니까?
Alexander Suraphel

3
@AlexanderSuraphel 소수점 위도와 경도를 사용하는 경우 IEEE float는 가장 작은 자릿수의 오류에 취약합니다. 예를 들어 위도 및 경도는 1 미터의 정밀도를 가질 수 있지만 100 미터 이상의 오류가있을 수 있습니다. 계산에 사용하는 경우 특히 그렇습니다.
Lonny Eachus 5
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.