문제

나는 통화, 요금 등을 다루는 코드를 작성하고 있습니다. 나는 수학과 저장에 큰 클래스를 사용할 것이지만, 우리는 그것으로 이상한 것을 달렸습니다.

이 진술 :

1876.8 == BigDecimal('1876.8')

거짓을 반환합니다.

서식 문자열을 통해 해당 값을 실행하면 "%.13f" 나는 얻다:

"%.20f" % 1876.8 => 1876.8000000000000
"%.20f" % BigDecimal('1876.8') => 1876.8000000000002

추가로 주목하십시오 2 마지막 소수점에서 큰 곳에서.

나는 Bigdecimal이 컴퓨터의 기본 부동 소수점에 직접 실수를 저장하는 부정확성에 대응해야한다고 생각했습니다. 이것은 어디에 있습니까? 2 에서 오는?

도움이 되었습니까?

해결책

당신은 옳습니다. Bigdecimal은 올바르게 저장해야합니다. 내 가장 좋은 추측은 다음과 같습니다.

  • Bigdecimal은 값을 올바르게 저장하고 있습니다
  • 문자열 서식 함수로 전달되면 Bigdecimal은 더 낮은 정밀 부동 소수점 값으로 캐스트되어 ... 02를 만듭니다.
  • 플로트와 직접 비교할 때, 플로트는 당신이 볼 수있는 20을 넘어서 더 십자 자리를 가지고 있습니다 (클래식 플로트는 동작을 비교할 수 없습니다).

어느 쪽이든, 당신은 플로트를 큰 부와 비교하는 정확한 결과를 얻지 못할 것입니다.

다른 팁

소수점 이하 자리의 수를 많이 통제하지는 않지만 BigDecimal의 기존 형식 메커니즘은 다음과 같습니다.

a.to_s('F')

더 많은 제어가 필요한 경우 도메인 문제가 대부분 통화에 관한 것이라고 가정하면 Money Gem 사용을 고려하십시오.

gem install money

평등을 위해 FPU 소수 문자열 분수를 비교하지 마십시오

문제는 분수를 포함하는 소수점 상수를 가진 부유 식 또는 이중 값의 평등 비교가 거의 성공하지 못한다는 것입니다.

이진 FP 표현에서 정확한 값을 갖는 소수점 스트링 분수는 거의 없으므로 평등 비교는 일반적으로 운명됩니다.*

당신의 정확한 질문에 답하기 위해 2 소수점 문자열 분수의 약간 다른 변환에서 Float 체재. 분수를 정확하게 표현할 수 없기 때문에 두 계산이 중간 계산에서 다른 양의 정밀도를 고려하고 궁극적으로 결과를 52 비트 IEEE 754 이중 정밀 Mantissa로 다르게 반올림 할 수 있습니다. 거기 때문에 중요하지 않습니다 ~이다 어쨌든 정확한 표현은 없지만 하나는 아마도 다른 것보다 더 잘못되었을 것입니다.

특히, 당신의 1876.8 FP 객체로 정확하게 표현할 수는 없습니다. 실제로 0.01 내지 0.99 사이에서 0.25, 0.50 및 0.75 만 정확한 이진 표현을 갖습니다. 다른 모든 사람들은 1876.8을 포함하고 영원히 반복하며 52 비트로 반올림됩니다. 이것이 Bigdecimal이 존재하는 이유의 절반 정도입니다. (이유의 나머지 절반은 FP 데이터의 고정 정밀도입니다. 때로는 더 필요합니다.)

따라서 실제 기계 값을 소수점 상수와 비교할 때 얻은 결과는 이진 분율의 모든 단일 비트에 따라 다릅니다 ... 1/2까지.52 ... 그리고 심지어 반올림이 필요합니다.

조금도 조금도 있다면 (hehe, 조금, 죄송합니다) 숫자를 생성 한 프로세스 또는 입력 변환 코드 또는 관련된 다른 항목에 대한 불완전한 것은 정확히 동일하게 보이지 않습니다.

비교에 대한 논쟁조차 할 수도 있습니다 ~해야 한다 IEEE-Format FPU가 해당 숫자를 정확하게 나타낼 수 없기 때문에 항상 실패합니다. 비록 그들이처럼 보이지만 그들은 실제로 같지 않습니다. 왼쪽에서는 소수 문자열이 바이너리 스트링으로 변환되었으며 대부분의 숫자는 정확히 변환되지 않습니다. 오른쪽에서는 여전히 소수점 문자열입니다.

따라서 부유물을 큰 부와 혼합하지 말고 하나의 큰 비교를 다른 큰 부드러운 것과 비교하십시오. (두 피연산자 모두 ~이다 플로트, 평등 테스트에는 훌륭한 관리 또는 퍼지 테스트가 필요합니다. 또한, 모든 형식의 숫자를 신뢰하지 마십시오. 출력 형식은 나머지가 분수의 오른쪽에서 벗어날 수 있으므로 일반적으로 0을보기 시작하지 않으면 쓰레기 값 만 볼 수 있습니다.)


*문제 : 기계 번호는 x/2입니다N, 그러나 소수점 상수는 x/(2입니다N * 5). 부호, 지수 및 Mantissa로서의 가치는 무한히 반복됩니다. 0 10000001001 1101010100110011001100110011001100110011001100110011... 아이러니하게도, FP 산술은 완벽하게 정확하며 값이 분수가 없을 때 평등 비교가 완벽하게 작동합니다.

데이비드가 말했듯이, Bigdecimal은 그것을 올바르게 저장하고 있습니다

 p (BigDecimal('1876.8') * 100000000000000).to_i

187680000000000000을 반환합니다

그렇습니다. 문자열 서식이 망치고 있습니다.

분수 센트가 필요하지 않은 경우 통화를 정수로 저장하고 조작하는 것을 고려한 다음 표시 할 때 100으로 나누십시오. 부동 소수점에 저장 및 조작의 피할 수없는 정밀 문제를 다루는 것보다 쉽다는 것을 알 수 있습니다.

Mac OS X에서는 실행 중입니다 ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9]

irb(main):004:0> 1876.8 == BigDecimal('1876.8') => true

그러나 루비 인 경우, 대상으로 전송 된 메시지 측면에서 생각해야한다고 생각합니다. 이것이 당신에게 무엇을 반환합니까 :

BigDecimal('1876.8') == 1876.8

두 사람은 동일하지 않으며, 정확한 소수점 평등을 결정하는 BigDecimal의 능력을 사용하려고한다면, 평등에 대한 메시지의 수신자 여야합니다.

같은 이유로 형식 메시지를 형식으로 보내어 BigDecimal을 형식화하는 것이 올바른 접근법이라고 생각하지 않습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top