2010-06-14 5 views
18

Ruby BigDecimal 유형 (정밀도와 스케일의 길이가 다를지라도)을 정확하게 계산해야하는지 또는 부동 소수점 헛수고를 예상해야합니까?Ruby BigDecimal 온 전성 체크 (부동 소수점 newb)

레일 애플리케이션의 모든 내 값은 BigDecimal이며 일부 오류가 발생합니다 (길이가 다릅니다). 내 메소드가 아니라 객체 유형이되기를 바랍니다.

답변

31

부동 소수점 연산으로 작업 할 때 두 가지 일반적인 함정이 있습니다.

첫번째 문제점은 루비 부동 소수점이 고정 된 정밀도를가집니다. 실제로 이것은 1) 너에게 아무런 문제가 없거나 2) 비참한 것, 3) 사이에있는 것 중 하나 다. 다음을 고려하십시오.

# float 
1.0e+25 - 9999999999999999900000000.0 
#=> 0.0 

# bigdecimal 
BigDecimal("1.0e+25") - BigDecimal("9999999999999999900000000.0") 
#=> 100000000 

정밀도 차이는 1 억입니다! 아주 심각 하죠?

정밀도 오류는 원래 숫자의 약 0.000000000000001 %입니다. 이것이 문제가되는지 아닌지를 결정하는 것은 당신에게 달려 있습니다. 그러나 임의의 정밀도를 가지기 때문에 BigDecimal을 사용하면 문제가 해결됩니다. 유일한 제한은 Ruby에서 사용할 수있는 메모리입니다.

두 번째 문제은 부동 소수점이 모든 분수를 정확하게 표현할 수 없다는 점입니다. 특히 그들은 십진수 부분에 문제가 있습니다. Ruby (및 대부분의 다른 언어)의 부동 소수점은 이진 부동 소수점이기 때문입니다. 예를 들어, 십진수 소수 0.2은 영원 반복 이진 소수 (0.001100110011...)입니다. 정밀도가 무엇이든 이진 부동 소수점에 정확하게 저장할 수 없습니다.

숫자를 반올림하는 경우 큰 차이가 발생할 수 있습니다. 고려 :

# float 
(0.29 * 50).round 
#=> 14 # not correct 

# bigdecimal 
(BigDecimal("0.29") * 50).round 
#=> 15 # correct 

BigDecimal 정확하게 진수 분수를 설명 할 수 있습니다. 그러나 십진수로도 정확하게 기술 할 수없는 분수가 있습니다. 예를 들어, 1/9은 영원히 반복되는 소수 부분입니다 (0.1111111111111...).

다시 말하지만, 번호를 반올림하면이 문제가 발생합니다. 고려 :

# bigdecimal 
(BigDecimal("1")/9 * 9/2).round 
#=> 0 # not correct 

를이 경우, 여전히는 반올림 오류를 줄 것이다 소수점 부동 포인트를 사용하여.

어떤 결론 : 당신이 (예를 들어, 돈) 소수 분수 계산을 할 경우

  • 진수 수레는 굉장하다.
  • 루비의 BigDecimal은 임의의 정밀도 부동 소수점이 필요하고 10 진수 또는 2 진 부동 소수점인지 상관하지 않습니다.
  • (과학) 데이터로 작업하는 경우 일반적으로 고정 된 정밀도의 숫자를 처리합니다. Ruby에 내장 된 수레가 아마도 충분할 것입니다.
  • 과 함께 산술 연산을 기대할 수는 없습니다. 모든 상황에서 정확하게 부동 소수점 유형이 정확해야합니다.
+1

"실제로이 문제는 1) 문제가되지 않거나 2) 비참한 것, 3) 중간에있는 것 중 하나입니다." –