연관성 수학 : (a + b) + c! = a + (b + c)
최근 에 Eric Lippert 의 오래된 블로그 게시물을 살펴 보았는데 , 연관성에 대해 글을 쓰는 동안 그는 C #에서 a, b, c의 특정 값과 (a + b) + c
동일하지 않다고 언급했습니다 a + (b + c)
.
나는 어떤 유형과 범위의 산술 값 이 참일 수 있고 그 이유를 알 수 없습니다.
double
유형 의 범위 :
double dbl1 = (double.MinValue + double.MaxValue) + double.MaxValue;
double dbl2 = double.MinValue + (double.MaxValue + double.MaxValue);
첫 번째는 double.MaxValue
이고 두 번째는double.Infinity
double
유형 의 정밀도 :
double dbl1 = (double.MinValue + double.MaxValue) + double.Epsilon;
double dbl2 = double.MinValue + (double.MaxValue + double.Epsilon);
이제 dbl1 == double.Epsilon
, 동안 dbl2 == 0
.
그리고 말 그대로 질문을 읽는 것에 :-)
에서 checked
모드 :
checked
{
int i1 = (int.MinValue + int.MaxValue) + int.MaxValue;
}
i1
이다 int.MaxValue
checked
{
int temp = int.MaxValue;
int i2 = int.MinValue + (temp + temp);
}
( temp
변수 사용에 유의하십시오 . 그렇지 않으면 컴파일러가 직접 오류를 발생시킵니다 ... 기술적으로 이것은 다른 결과가 될 것입니다. :-) 올바르게 컴파일하고 컴파일하지 않습니다.)
이것은 던졌습니다 OverflowException
... 결과는 다릅니다 :-) ( int.MaxValue
대 Exception
)
한 가지 예
a = 1e-30
b = 1e+30
c = -1e+30
극단의 작은 숫자와 큰 숫자로 다른 결과를 얻는 방법을 보여주는 다른 답변을 확장하면 실제 일반 숫자를 가진 부동 소수점이 다른 답변을 제공하는 예가 있습니다.
이 경우 극도의 정밀도 한계에서 숫자를 사용하는 대신 단순히 많은 추가 작업을 수행합니다. 차이점은 수행 (((...(((a+b)+c)+d)+e)...
또는...(((a+b)+(c+d))+((e+f)+(g+h)))+...
여기서 파이썬을 사용하고 있지만 C #으로 작성하면 동일한 결과를 얻을 수 있습니다. 먼저 모두 0.1 인 백만 개의 값 목록을 만듭니다. 왼쪽에서 추가하면 반올림 오류가 중요해집니다.
>>> numbers = [0.1]*1000000
>>> sum(numbers)
100000.00000133288
이제 다시 추가하되 이번에는 쌍으로 추가합니다 (중간 스토리지를 덜 사용하는 훨씬 더 효율적인 방법이 있지만 여기서는 구현을 간단하게 유지했습니다).
>>> def pair_sum(numbers):
if len(numbers)==1:
return numbers[0]
if len(numbers)%2:
numbers.append(0)
return pair_sum([a+b for a,b in zip(numbers[::2], numbers[1::2])])
>>> pair_sum(numbers)
100000.0
이번에는 반올림 오류가 최소화됩니다.
완전성을 위해 편집 하십시오. 여기에 더 효율적이지만 쌍별 합계 구현을 따르기가 쉽지 않습니다. pair_sum()
위와 같은 대답을 제공합니다 .
def pair_sum(seq):
tmp = []
for i,v in enumerate(seq):
if i&1:
tmp[-1] = tmp[-1] + v
i = i + 1
n = i & -i
while n > 2:
t = tmp.pop(-1)
tmp[-1] = tmp[-1] + t
n >>= 1
else:
tmp.append(v)
while len(tmp) > 1:
t = tmp.pop(-1)
tmp[-1] = tmp[-1] + t
return tmp[0]
다음은 C #으로 작성된 간단한 pair_sum입니다.
using System;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static double pair_sum(double[] numbers)
{
if (numbers.Length==1)
{
return numbers[0];
}
var new_numbers = new double[(numbers.Length + 1) / 2];
for (var i = 0; i < numbers.Length - 1; i += 2) {
new_numbers[i / 2] = numbers[i] + numbers[i + 1];
}
if (numbers.Length%2 != 0)
{
new_numbers[new_numbers.Length - 1] = numbers[numbers.Length-1];
}
return pair_sum(new_numbers);
}
static void Main(string[] args)
{
var numbers = new double[1000000];
for (var i = 0; i < numbers.Length; i++) numbers[i] = 0.1;
Console.WriteLine(numbers.Sum());
Console.WriteLine(pair_sum(numbers));
}
}
}
출력 포함 :
100000.000001333
100000
이는 일반 값 유형 (int, long 등)이 고정 된 양의 바이트를 사용하여 저장된다는 사실에서 기인합니다. 따라서 두 값의 합이 바이트 저장 용량을 초과하면 오버플로가 발생할 수 있습니다.
C #에서는 이러한 종류의 문제를 피하기 위해 BigInteger 를 사용할 수 있습니다 . BigInteger는 크기가 임의적이므로 오버플로를 생성하지 않습니다.
BigInteger는 .NET 4.0 이상 (VS 2010+)에서만 사용할 수 있습니다.
짧은 대답은 (a + b) + c == a + (b + c)
수학적인 것이지만 반드시 계산적인 것은 아닙니다.
컴퓨터는 실제로 이진으로 작동한다는 것을 기억하고, 단순한 십진수라도 내부 형식으로 변환 할 때 반올림 오류가 발생할 수 있습니다.
언어에 따라 더하기 만해도 반올림 오류가 발생할 수 있으며 위의 예에서의 반올림 오류는의 반올림 오류와 a+b
다를 수 있습니다 b+c
.
한 가지 놀라운 범죄자는 JavaScript 0.1 + 0.2 != 0.3
입니다. 반올림 오차는 소수점 이하로 멀지 만 실제적이고 문제가 있습니다.
작은 부분을 먼저 추가하여 반올림 오차를 줄이는 것이 일반적인 원칙입니다. 이렇게하면 더 많은 숫자에 압도되기 전에 누적 될 수 있습니다.
유사한 몇 가지 예 :
static void A(string s, int i, int j)
{
var test1 = (s + i) + j;
var test2 = s + (i + j);
var testX = s + i + j;
}
다음 A("Hello", 3, 5)
에 리드 test1
와 testX
동등 인 "Hello35"
반면,이 test2
될 것이다 "Hello8"
.
과:
static void B(int i, int j, long k)
{
var test1 = (i + j) + k;
var test2 = i + (j + k);
var testX = i + j + k;
}
여기서 B(2000000000, 2000000000, 42L)
리드 test1
와 testX
동등 인 -294967254L
통상의 unchecked
상태, 모드 test2
가된다 4000000042L
.
참조 URL : https://stackoverflow.com/questions/32004190/associativity-math-abcabc
'programing' 카테고리의 다른 글
NumPy : 밑이 n 인 로그 (0) | 2021.01.19 |
---|---|
배열에 주어진 값이 포함 된 항목 필터링 (0) | 2021.01.19 |
Golang을 사용하여 디렉토리의 모든 내용을 제거하는 방법은 무엇입니까? (0) | 2021.01.19 |
Proguard 문제 "경고 : 익명 내부 클래스에 대한 InnerClasses 속성 무시" (0) | 2021.01.19 |
다른 .EXE 파일을 실행하는 .BAT 파일의 명령 창 숨기기 (0) | 2021.01.19 |