문제
간단한 C# 기능이 있습니다.
public static double Floor(double value, double step)
{
return Math.Floor(value / step) * step;
}
이는 "값"보다 낮거나 동일 한 더 높은 숫자를 "단계"의 배수입니다. 그러나 다음 테스트에서 볼 수 있듯이 정밀도가 부족합니다.
[TestMethod()]
public void FloorTest()
{
int decimals = 6;
double value = 5F;
double step = 2F;
double expected = 4F;
double actual = Class.Floor(value, step);
Assert.AreEqual(expected, actual);
value = -11.5F;
step = 1.1F;
expected = -12.1F;
actual = Class.Floor(value, step);
Assert.AreEqual(Math.Round(expected, decimals),Math.Round(actual, decimals));
Assert.AreEqual(expected, actual);
}
첫 번째와 두 번째 어설 즈는 괜찮지 만 세 번째는 실패합니다. 결과는 6 번째 십진 자리까지 동일하기 때문입니다. 왜 그런 겁니까? 이것을 수정할 방법이 있습니까?
업데이트 테스트를 디버그하면 값이 6 번째 대신 8 번째 십진 자리까지 값이 동일하다는 것을 알 수 있습니다. 아마도 Math.round가 약간의 부정확성을 소개하기 때문일 수 있습니다.
메모 내 테스트 코드에서 나는 "F"접미사 (명시 적 플로트 상수)를 썼으며, 여기서 "D"(Double)를 의미하므로 변경하면 더 정밀도를 가질 수 있습니다.
해결책
모든 f 포스트 픽스를 생략하면 (즉 -12.1
대신에 -12.1F
) 당신은 몇 자릿수에 평등을 얻을 것입니다. 상수 (및 특히 예상 값)는 이제 F
. 의도적으로 그렇게한다면 설명 해주세요.
그러나 나머지는 평등에 대한 이중 또는 플로트 값을 비교할 때 다른 답변과 동의하지만 신뢰할 수 없습니다.
다른 팁
나는 실제로 그들이 플로트와 복식을 위해 == 운영자를 구현하지 않았 으면 좋겠다. 더블 또는 플로트가 다른 값과 동일인지 묻는 것은 거의 항상 잘못된 일입니다.
컴퓨터의 부동 소수점 산술은 정확한 과학이 아닙니다 :).
사전 정의 된 소수의 소수에 대한 정확한 정밀도를 원한다면 두 배 대신 소수점을 사용하거나 사소한 간격을 수용하십시오.
http://en.wikipedia.org/wiki/floating_point#accuracy_problems
예를 들어, 0.1 및 0.01 (이진)의 비 표현 가능성은 0.1을 제곱하려는 시도가 0.01이거나 가장 가까운 대표적인 수가 아니라는 것을 의미합니다.
숫자 시스템의 기계 해석 (이진)을 원하는 경우 플로팅 포인트 만 사용하십시오. 당신은 10 센트를 대표 할 수 없습니다.
정밀도를 원한다면 System.decimal을 사용하십시오. 속도를 원한다면 System.Double (또는 System.Float)을 사용하십시오. 부동 소수점 번호는 "무한 정밀도"숫자가 아니므로 평등에는 공차가 포함되어야합니다. 숫자에 합리적인 숫자가있는 한 괜찮습니다.
- 매우 크고 작은 숫자로 수학을하고 싶다면 float 또는 double을 사용하지 마십시오.
- 무한한 정밀도가 필요한 경우 플로트 나 이중을 사용하지 마십시오.
- 매우 많은 수의 값을 집계하는 경우 Float 또는 Double을 사용하지 마십시오 (오류는 스스로 복합적으로 나타납니다).
- 속도와 크기가 필요한 경우 플로트 또는 이중을 사용하십시오.
보다 이것 정밀도가 수학 연산의 결과에 어떤 영향을 미치는지에 대한 자세한 분석에 대한 답변 (나도).
이 질문에 대한 답을 확인하십시오. 평등에 대한 부동 소수점 값을 0으로 확인하는 것이 안전합니까?
정말로, "허용 내에서 ..."를 확인하십시오.
플로트와 복식은 모든 숫자를 정확하게 저장할 수 없습니다. 이것은 IEEE 플로팅 포인트 시스템의 제한입니다. 충실한 정밀도를 갖기 위해서는보다 고급 수학 라이브러리를 사용해야합니다.
특정 지점을지나 정밀도가 필요하지 않으면 10 진수가 더 잘 작동 할 것입니다. 두 배보다 정밀도가 높습니다.
비슷한 문제의 경우, 대부분의 테스트 사례 (최대 5 자리 정밀도)의 성공으로 보이는 다음 구현을 사용하게됩니다.
public static double roundValue(double rawValue, double valueTick)
{
if (valueTick <= 0.0) return 0.0;
Decimal val = new Decimal(rawValue);
Decimal step = new Decimal(valueTick);
Decimal modulo = Decimal.Round(Decimal.Divide(val,step));
return Decimal.ToDouble(Decimal.Multiply(modulo, step));
}