(.1f + .2f ==. 3f)! = (.1f + .2f) .Equals (.3f) 왜?
내 질문은 부동 정밀도에 관한 것이 아닙니다 . Equals()
와 다른 이유에 대한 것 ==
입니다.
나는 이유를 이해 .1f + .2f == .3f
하다 false
(반면이 .1m + .2m == .3m
있다 true
).
나는 그것이 ==
참조이며 .Equals()
가치 비교입니다. ( 편집 : 이것에 더 많은 것이 있음을 알고 있습니다.)
그런데 왜 (.1f + .2f).Equals(.3f)
true
, while (.1d+.2d).Equals(.3d)
is still false
?
.1f + .2f == .3f; // false
(.1f + .2f).Equals(.3f); // true
(.1d + .2d).Equals(.3d); // false
질문은 헷갈 리게 표현되어 있습니다. 이를 여러 개의 작은 질문으로 나누겠습니다.
왜 1/10 더하기 2/10이 부동 소수점 산술에서 항상 3/10과 같지는 않습니까?
비유를하겠습니다. 모든 숫자가 정확히 소수점 다섯 자리로 반올림되는 수학 시스템이 있다고 가정합니다. 다음과 같이 말한다고 가정합니다.
x = 1.00000 / 3.00000;
x는 0.33333이 될 것으로 예상 할 것입니다. 맞죠? 그것이 우리 시스템에서 실제 답 에 가장 가까운 숫자 이기 때문입니다 . 이제 당신이 말했다고 가정
y = 2.00000 / 3.00000;
y가 0.66667이 될 것으로 예상 할 것입니다. 맞죠? 왜냐하면 그것은 우리 시스템에서 실제 답 에 가장 가까운 숫자 이기 때문입니다 . 0.66666은 0.66667 보다 2/3에서 더 멀다 .
첫 번째 경우에는 반올림하고 두 번째 경우에는 반올림했습니다.
이제 우리가 말할 때
q = x + x + x + x;
r = y + x + x;
s = y + y;
우리는 무엇을 얻습니까? 우리가 정확한 산술을했다면 이들 각각은 분명히 4/3가 될 것이고 모두 같을 것입니다. 그러나 그들은 동등하지 않습니다. 1.33333이 우리 시스템에서 4/3에 가장 가까운 숫자이지만 r만이 그 값을가집니다.
q는 1.33332입니다. x가 조금 작았 기 때문에 모든 추가에 해당 오류가 누적되고 최종 결과가 너무 작습니다. 마찬가지로 s는 너무 큽니다. 이것은 1.33334입니다. y가 약간 너무 컸기 때문입니다. r은 y의 너무 큰 정도가 x의 너무 작은 정도에 의해 취소되고 결과가 정확 해지기 때문에 정답을 얻습니다.
정밀도 자릿수가 오류의 크기와 방향에 영향을 줍니까?
예; 정밀도가 높으면 오류의 크기가 작아 지지만 계산에서 오류로 인해 손실 또는 이득이 발생하는지 여부를 변경할 수 있습니다. 예를 들면 :
b = 4.00000 / 7.00000;
b는 0.57143이고, 이는 실제 값인 0.571428571에서 반올림됩니다. 만약 우리가 0.57142857이 될 여덟 곳으로 갔더라면, 오차의 크기는 훨씬 더 작지만 반대 방향입니다. 반올림되었습니다.
정밀도를 변경하면 오류가 각 개별 계산에서 이득인지 손실인지가 변경 될 수 있으므로 주어진 집계 계산의 오류가 서로를 강화할지 아니면 서로를 취소 할지를 변경할 수 있습니다. 최종 결과는 때때로 낮은 정밀도 계산이 더 높은 정밀도 계산보다 "진정한"결과에 더 가깝다는 것입니다. 왜냐하면 낮은 정밀도 계산 에서는 운이 좋고 오류가 다른 방향에 있기 때문입니다 .
더 높은 정밀도로 계산을하면 항상 실제 답에 더 가까운 답을 얻을 수 있지만이 주장은 그렇지 않습니다. 이것은 왜 때때로 float 계산이 "올바른"답을 제공하지만 정밀도가 두 배인 double 계산이 "잘못된"답을 제공하는 이유를 설명합니다. 맞습니까?
예, 이것은 십진 정밀도의 5 자리 대신 특정 숫자의 이진 정밀도를 갖는 것을 제외하고는 귀하의 예에서 정확히 발생하는 것입니다 . 3 분의 1을 5 자리 또는 유한 한 숫자로 정확하게 표현할 수없는 것처럼 0.1, 0.2, 0.3은 유한 한 이진수로 정확하게 표현할 수 없습니다. 이들 중 일부는 반올림되고 일부는 반올림되며, 이들 중 일부를 추가 하면 오류가 증가 하는지 또는 오류가 취소 되는지 여부 는 각 시스템에있는 이진 숫자 의 특정 세부 사항에 따라 다릅니다 . 즉, 정밀도의 변화는 답을 바꿀 수 있습니다.좋든 나쁘 든. 일반적으로 정밀도가 높을수록 정답에 더 가깝지만 항상 그런 것은 아닙니다.
float 및 double이 이진 숫자를 사용하는 경우 정확한 십진 산술 계산을 어떻게 얻을 수 있습니까?
정확한 십진 수학이 필요한 경우 decimal
유형 을 사용하십시오 . 이진 분수가 아닌 소수를 사용합니다. 지불하는 대가는 상당히 크고 느리다는 것입니다. 물론 우리가 이미 보았 듯이 1/3 또는 4-7과 같은 분수는 정확하게 표현되지 않을 것입니다. 그러나 실제로 소수 인 분수는 최대 약 29 개의 유효 자릿수까지 제로 오류로 표시됩니다.
좋아, 나는 모든 부동 소수점 체계가 표현 오류로 인해 부정확성을 도입하고, 그러한 부정확성이 때때로 계산에 사용 된 정밀도 비트 수에 따라 서로 누적되거나 취소 될 수 있음을 인정합니다. 최소한 이러한 부정확성이 일관 될 것이라는 보장이 있습니까?
아니요, 수 레나 복식에 대해 그러한 보장이 없습니다. 컴파일러와 런타임 모두 사양에서 요구하는 것보다 더 높은 정밀도로 부동 소수점 계산을 수행 할 수 있습니다. 특히 컴파일러와 런타임은 64 비트 또는 80 비트 또는 128 비트 또는 원하는 32 비트보다 큰 비트로 단 정밀도 (32 비트) 산술 을 수행 할 수 있습니다.
컴파일러와 런타임은 그렇게 할 수 있지만 당시에 는 그렇게 생각합니다 . 기계마다, 실행마다 일관 될 필요는 없습니다. 이것은 계산을 더 정확하게 할 수있을 뿐이므로 버그로 간주되지 않습니다. 기능입니다. 예상대로 작동하는 프로그램을 작성하기가 매우 어렵게 만드는 기능이지만 그럼에도 불구하고 기능입니다.
즉, 리터럴 0.1 + 0.2와 같이 컴파일 타임에 수행 된 계산이 런타임에 변수를 사용하여 수행되는 동일한 계산과 다른 결과를 제공 할 수 있다는 의미입니까?
네.
무엇의 결과 비교에 대한
0.1 + 0.2 == 0.3
에를(0.1 + 0.2).Equals(0.3)
?
첫 번째는 컴파일러에 의해 계산되고 두 번째는 런타임에 의해 계산되기 때문에 나는 그들이 원하는대로 사양에서 요구하는 것보다 더 많은 정밀도를 임의로 사용할 수 있다고 말했습니다. 그렇습니다. 아마도 그들 중 하나는 64 비트 정밀도로만 계산을 수행하는 반면 다른 하나는 계산의 일부 또는 전체에 대해 80 비트 또는 128 비트 정밀도를 선택하고 다른 답을 얻습니다.
그러니 여기서 잠시 기다려주세요. 당신은 그것
0.1 + 0.2 == 0.3
과 다를 수있을 뿐만 아니라(0.1 + 0.2).Equals(0.3)
. 당신은 그 말을하는지0.1 + 0.2 == 0.3
완전히 컴파일러의 변덕에 참 또는 거짓으로 계산 될 수있다. 화요일에는 true를, 목요일에는 false를 생성 할 수 있습니다. 한 시스템에서는 true를 생성하고 다른 시스템에서는 false를 생성 할 수 있습니다. 같은 프로그램에서 표현식이 두 번 나타나면 true와 false를 모두 생성 할 수 있습니다. 이 표현식은 어떤 이유로 든 값을 가질 수 있습니다. 컴파일러는 여기에서 완전히 신뢰할 수 없습니다.
옳은.
일반적으로 이것이 C # 컴파일러 팀에보고되는 방식은 누군가가 디버그에서 컴파일 할 때 true를 생성하고 릴리스 모드에서 컴파일 할 때 false를 생성하는 표현식을 가지고 있다는 것입니다. 디버그 및 릴리스 코드 생성이 레지스터 할당 체계를 변경하기 때문에 이것이 발생하는 가장 일반적인 상황입니다. 그러나 컴파일러가되어 허용 너무 오래는 참 또는 거짓 선택으로,이 표현으로는 좋아 아무것도 할 수 있습니다. (즉, 컴파일 타임 오류를 생성 할 수 없습니다.)
이것은 광기입니다.
옳은.
이 엉망진창에 대해 내가 누구를 비난해야합니까?
내가 아니야, 그건 확실히.
인텔은 일관된 결과를 내기 위해 훨씬 더 비싼 부동 소수점 수학 칩을 만들기로 결정했습니다. 등록 할 작업과 스택에 보관할 작업에 대한 컴파일러의 작은 선택은 결과에 큰 차이를 가져올 수 있습니다.
일관된 결과를 보장하려면 어떻게해야합니까?
decimal
내가 전에 말했듯 이 유형을 사용하십시오 . 또는 모든 수학을 정수로 수행하십시오.
복식이나 수레를 사용해야합니다. 일관된 결과를 장려하기 위해 무엇 을 할 수 있습니까?
Yes. If you store any result into any static field, any instance field of a class or array element of type float or double then it is guaranteed to be truncated back to 32 or 64 bit precision. (This guarantee is expressly not made for stores to locals or formal parameters.) Also if you do a runtime cast to (float)
or (double)
on an expression that is already of that type then the compiler will emit special code that forces the result to truncate as though it had been assigned to a field or array element. (Casts which execute at compile time -- that is, casts on constant expressions -- are not guaranteed to do so.)
To clarify that last point: does the C# language specification make those guarantees?
No. The runtime guarantees that stores into an array or field truncate. The C# specification does not guarantee that an identity cast truncates but the Microsoft implementation has regression tests that ensure that every new version of the compiler has this behaviour.
All the language spec has to say on the subject is that floating point operations may be performed in higher precision at the discretion of the implementation.
When you write
double a = 0.1d;
double b = 0.2d;
double c = 0.3d;
Actually, these are not exactly 0.1
, 0.2
and 0.3
. From IL code;
IL_0001: ldc.r8 0.10000000000000001
IL_000a: stloc.0
IL_000b: ldc.r8 0.20000000000000001
IL_0014: stloc.1
IL_0015: ldc.r8 0.29999999999999999
There are a lof of question in SO pointing that issue like (Difference between decimal, float and double in .NET? and Dealing with floating point errors in .NET) but I suggest you to read cool article called;
What Every Computer Scientist Should Know About Floating-Point Arithmetic
Well, what leppie said is more logical. The real situation is here, totaly depends on compiler
/ computer
or cpu
.
Based on leppie code, this code works on my Visual Studio 2010 and Linqpad, as a result True
/False
, but when I tried it on ideone.com, the result will be True
/True
Check the DEMO.
Tip: When I wrote Console.WriteLine(.1f + .2f == .3f);
Resharper warnings me;
Comparison of floating points number with equality operator. Possible loss of precision while rounding values.
As said in the comments, this is due to the compiler doing constant propagation and performing the calculation at a higher precision (I believe this is CPU dependent).
var f1 = .1f + .2f;
var f2 = .3f;
Console.WriteLine(f1 == f2); // prints true (same as Equals)
Console.WriteLine(.1f+.2f==.3f); // prints false (acts the same as double)
@Caramiriel also points out that .1f+.2f==.3f
is emit as false
in the IL, hence the compiler did the calculation at compile-time.
To confirm the constant folding/propagation compiler optimization
const float f1 = .1f + .2f;
const float f2 = .3f;
Console.WriteLine(f1 == f2); // prints false
FWIW following test passes
float x = 0.1f + 0.2f;
float result = 0.3f;
bool isTrue = x.Equals(result);
bool isTrue2 = x == result;
Assert.IsTrue(isTrue);
Assert.IsTrue(isTrue2);
So problem is actually with this line
0.1f + 0.2f==0.3f
Which as stated is probably compiler/pc specific
Most people are jumping at this question from wrong angle I think so far
UPDATE:
Another curious test I think
const float f1 = .1f + .2f;
const float f2 = .3f;
Assert.AreEqual(f1, f2); passes
Assert.IsTrue(f1==f2); doesnt pass
Single equality implementation:
public bool Equals(float obj)
{
return ((obj == this) || (IsNaN(obj) && IsNaN(this)));
}
==
is about comparing exact floats values.
Equals
is a boolean method that may return true or false. The specific implementation may vary.
I don't know why but at this time some results of mine are different from yours. Note the third and fourth test happens to be contrary to the problem, so parts of your explanations might be wrong now.
using System;
class Test
{
static void Main()
{
float a = .1f + .2f;
float b = .3f;
Console.WriteLine(a == b); // true
Console.WriteLine(a.Equals(b)); // true
Console.WriteLine(.1f + .2f == .3f); // true
Console.WriteLine((1f + .2f).Equals(.3f)); //false
Console.WriteLine(.1d + .2d == .3d); //false
Console.WriteLine((1d + .2d).Equals(.3d)); //false
}
}
참고URL : https://stackoverflow.com/questions/15117037/1f-2f-3f-1f-2f-equals-3f-why
'Nice programing' 카테고리의 다른 글
catch 및 finally에서 return 문 동작 (0) | 2020.11.10 |
---|---|
ng-repeat를 사용하여 AngularJS에서 맵 항목을 반복하는 방법 (0) | 2020.11.10 |
Android에서 게임 클라이언트 초기화 (0) | 2020.11.10 |
Golang에서지도의 동등성을 테스트하는 방법은 무엇입니까? (0) | 2020.11.10 |
JSON.net으로 JToken의 이름 / 키 얻기 (0) | 2020.11.10 |