[c] 배열의 중간을 계산할 때 왜 start + (end-start) / 2 over (start + end) / 2를 선호합니까?

프로그래머가 수식을 사용하는 것을 보았습니다.

mid = start + (end - start) / 2

더 간단한 공식을 사용하는 대신

mid = (start + end) / 2

배열 또는 목록에서 중간 요소를 찾는 데 사용됩니다.

그들은 왜 전자를 사용합니까?



답변

세 가지 이유가 있습니다.

우선, 1을 오버플 start + (end - start) / 2end - start하지 않는 한 포인터를 사용하더라도 작동합니다 .

int *start = ..., *end = ...;
int *mid = start + (end - start) / 2; // works as expected
int *mid = (start + end) / 2;         // type error, won't compile

둘째로, start + (end - start) / 2하지 오버 플로우 경우 것입니다 startend큰 긍정적 인 숫자입니다. 부호있는 피연산자를 사용하면 오버플로가 정의되지 않습니다.

int start = 0x7ffffffe, end = 0x7fffffff;
int mid = start + (end - start) / 2; // works as expected
int mid = (start + end) / 2;         // overflow... undefined

(이 end - start경우 오버플로 가 발생할 수 있지만 start < 0또는 경우에만 해당됩니다 end < 0.)

또는 부호없는 산술을 사용하면 오버플로가 정의되지만 잘못된 대답을 제공합니다. 그러나 부호없는 피연산자의 경우 start + (end - start) / 2에는 오버플로되지 않습니다 end >= start.

unsigned start = 0xfffffffeu, end = 0xffffffffu;
unsigned mid = start + (end - start) / 2; // works as expected
unsigned mid = (start + end) / 2;         // mid = 0x7ffffffe

마지막으로, 당신은 종종 start요소를 향해 반올림하려고합니다 .

int start = -3, end = 0;
int mid = start + (end - start) / 2; // -2, closer to start
int mid = (start + end) / 2;         // -1, surprise!

각주

1 C 표준에 따르면 포인터 빼기 결과를로 표현할 수없는 ptrdiff_t경우 동작이 정의되지 않습니다. 그러나 실제로 char는 전체 주소 공간의 절반 이상을 사용하여 배열을 할당해야 합니다.


답변

이 사실을 보여주기 위해 간단한 예를 들어 보겠습니다. 특정 배열에서 range의 중간 점을 찾으려고 한다고 가정 합니다 [1000, INT_MAX]. 이제 데이터 유형이 저장할 수 INT_MAX있는 가장 큰 값 int입니다. 경우에도 1이 추가되어, 최종 값은 음수가 될 것이다.

또한, start = 1000end = INT_MAX.

수식 사용 : (start + end)/2,

중간 점은

(1000 + INT_MAX)/2= -(INT_MAX+999)/2, 어떤 부정세그먼트 오류를 제공 할 수 있습니다 우리는이 값을 사용하여 인덱스하려고합니다.

그러나 공식을 사용하면 다음과 (start + (end-start)/2)같은 이점이 있습니다.

(1000 + (INT_MAX-1000)/2)= (1000 + INT_MAX/2 - 500)= (INT_MAX/2 + 500) 있는 오버 플로우하지 않습니다 .


답변

다른 사람들이 이미 말한 것을 더하기 위해 첫 번째 것은 수학적으로 덜 생각하는 사람들에게 그 의미를 명확하게 설명합니다.

mid = start + (end - start) / 2

다음과 같이 읽습니다.

중간은 시작에 길이의 절반을 더한 것과 같습니다.

이므로:

mid = (start + end) / 2

다음과 같이 읽습니다.

중간은 시작 + 끝의 절반과 같습니다.

적어도 그렇게 표현할 때 첫 번째만큼 명확하지 않은 것 같습니다.

Kos가 지적했듯이 다음과 같이 읽을 수도 있습니다.

중간은 시작과 끝의 평균과 같습니다.

적어도 내 의견으로는 첫 번째만큼 분명하지만 명확하지는 않습니다.


답변

start + (end-start) / 2는 가능한 오버플로를 피할 수 있습니다 (예 : start = 2 ^ 20 및 end = 2 ^ 30).


답변