start + (end - start) / 2También tiene un significado semántico: (end - start)es la longitud, por lo que este dice: start + half the length.
njzk2
2
@ LưuVĩnhPhúc: ¿Esta pregunta no tiene las mejores respuestas y la mayoría de los votos? Si es así, las otras preguntas probablemente deberían cerrarse como un duplicado de esta. La edad de las publicaciones es irrelevante.
Nisse Engström
Respuestas:
218
Hay tres razones
En primer lugar, start + (end - start) / 2funciona incluso si está utilizando punteros, siempre que end - startno se desborde 1 .
int*start =...,*end =...;int*mid = start +(end - start)/2;// works as expectedint*mid =(start + end)/2;// type error, won't compile
En segundo lugar, start + (end - start) / 2¿No desbordamiento si starty endson grandes números positivos. Con operandos firmados, el desbordamiento no está definido:
int start =0x7ffffffe, end =0x7fffffff;int mid = start +(end - start)/2;// works as expectedint mid =(start + end)/2;// overflow... undefined
(Tenga en cuenta que end - startpuede desbordarse, pero solo si start < 0o end < 0.)
O con aritmética sin signo, se define el desbordamiento pero le da la respuesta incorrecta. Sin embargo, para operandos sin firmar, start + (end - start) / 2nunca se desbordará mientras end >= start.
unsigned start =0xfffffffeu, end =0xffffffffu;unsigned mid = start +(end - start)/2;// works as expectedunsigned mid =(start + end)/2;// mid = 0x7ffffffe
Finalmente, a menudo quieres redondear hacia el startelemento.
int start =-3, end =0;int mid = start +(end - start)/2;// -2, closer to startint mid =(start + end)/2;// -1, surprise!
Notas al pie
1 Según el estándar C, si el resultado de la resta del puntero no es representable como a ptrdiff_t, entonces el comportamiento es indefinido. Sin embargo, en la práctica, esto requiere asignar una charmatriz utilizando al menos la mitad del espacio de direcciones completo.
El resultado de (end - start)en el signed intcaso es indefinido cuando se desborda.
ensc
¿Puedes demostrar que end-startno se desbordará? AFAIK si toma un negativo start, debería ser posible hacer que se desborde. Claro, la mayoría de las veces cuando calculas el promedio sabes que los valores son >= 0...
Bakuriu
12
@Bakuriu: Es imposible probar algo que no es cierto.
Dietrich Epp
44
Es de particular interés en C, ya que la resta del puntero (según el estándar) se rompe por diseño. Se permite que las implementaciones creen matrices tan grandes que end - startno estén definidas, porque los tamaños de los objetos no están firmados, mientras que las diferencias de puntero están firmadas. Entonces, end - start"funciona incluso utilizando punteros", siempre que de alguna manera también mantenga el tamaño de la matriz a continuación PTRDIFF_MAX. Para ser justos con el estándar, eso no es una gran obstrucción en la mayoría de las arquitecturas, ya que es la mitad del tamaño del mapa de memoria.
Steve Jessop
3
@Bakuriu: Por cierto, hay un botón "editar" en la publicación que puedes usar para sugerir cambios (o hacerlos tú mismo) si crees que me he perdido algo o algo no está claro. Solo soy humano, y esta publicación ha sido vista por más de dos mil pares de globos oculares. El tipo de comentario, "Deberías aclarar ..." realmente me molesta.
Dietrich Epp
18
Podemos tomar un ejemplo simple para demostrar este hecho. Supongamos que en una determinada matriz grande , estamos tratando de encontrar el punto medio del rango [1000, INT_MAX]. Ahora, INT_MAXes el valor más grande que intpuede almacenar el tipo de datos. Incluso si 1se agrega a esto, el valor final será negativo.
Además, start = 1000y end = INT_MAX.
Utilizando la fórmula: (start + end)/2,
el punto medio será
(1000 + INT_MAX)/2= -(INT_MAX+999)/2, que es negativo y puede dar un error de segmentación si intentamos indexar utilizando este valor.
Pero, usando la fórmula (start + (end-start)/2), obtenemos:
(1000 + (INT_MAX-1000)/2)= (1000 + INT_MAX/2 - 500)= (INT_MAX/2 + 500)que no se desbordará .
Entiendo tu punto, pero esto realmente es un tramo. Si ve "e - s" y piensa "longitud", entonces seguramente verá "(s + e) / 2" y piensa "promedio" o "medio".
djechlin
2
@djechlin Los programadores son pobres en matemáticas. Están ocupados haciendo su trabajo. No tienen tiempo para asistir a las clases de matemáticas.
Little Alien
1
start + (end-start) / 2 puede evitar un posible desbordamiento, por ejemplo start = 2 ^ 20 y end = 2 ^ 30
(start + end)puede desbordarse, mientras(end - start)que no puede.startyendson puntero.start + (end - start) / 2También tiene un significado semántico:(end - start)es la longitud, por lo que este dice:start + half the length.Respuestas:
Hay tres razones
En primer lugar,
start + (end - start) / 2funciona incluso si está utilizando punteros, siempre queend - startno se desborde 1 .En segundo lugar,
start + (end - start) / 2¿No desbordamiento sistartyendson grandes números positivos. Con operandos firmados, el desbordamiento no está definido:(Tenga en cuenta que
end - startpuede desbordarse, pero solo sistart < 0oend < 0.)O con aritmética sin signo, se define el desbordamiento pero le da la respuesta incorrecta. Sin embargo, para operandos sin firmar,
start + (end - start) / 2nunca se desbordará mientrasend >= start.Finalmente, a menudo quieres redondear hacia el
startelemento.Notas al pie
1 Según el estándar C, si el resultado de la resta del puntero no es representable como a
ptrdiff_t, entonces el comportamiento es indefinido. Sin embargo, en la práctica, esto requiere asignar unacharmatriz utilizando al menos la mitad del espacio de direcciones completo.fuente
(end - start)en elsigned intcaso es indefinido cuando se desborda.end-startno se desbordará? AFAIK si toma un negativostart, debería ser posible hacer que se desborde. Claro, la mayoría de las veces cuando calculas el promedio sabes que los valores son>= 0...end - startno estén definidas, porque los tamaños de los objetos no están firmados, mientras que las diferencias de puntero están firmadas. Entonces,end - start"funciona incluso utilizando punteros", siempre que de alguna manera también mantenga el tamaño de la matriz a continuaciónPTRDIFF_MAX. Para ser justos con el estándar, eso no es una gran obstrucción en la mayoría de las arquitecturas, ya que es la mitad del tamaño del mapa de memoria.Podemos tomar un ejemplo simple para demostrar este hecho. Supongamos que en una determinada matriz grande , estamos tratando de encontrar el punto medio del rango
[1000, INT_MAX]. Ahora,INT_MAXes el valor más grande queintpuede almacenar el tipo de datos. Incluso si1se agrega a esto, el valor final será negativo.Además,
start = 1000yend = INT_MAX.Utilizando la fórmula:
(start + end)/2,el punto medio será
Pero, usando la fórmula
(start + (end-start)/2), obtenemos:fuente
INT_MAX, el resultado no será negativo, sino indefinido.INT_MAXa-INT_MAX. Sin embargo, es un mal hábito confiar en eso.Para agregar a lo que otros ya han dicho, el primero explica su significado más claramente para aquellos con una mentalidad menos matemática:
se lee como:
mientras:
se lee como:
Lo que no parece tan claro como el primero, al menos cuando se expresa así.
como señaló Kos, también puede leer:
Lo cual es más claro pero aún no, al menos en mi opinión, tan claro como el primero.
fuente
start + (end-start) / 2 puede evitar un posible desbordamiento, por ejemplo start = 2 ^ 20 y end = 2 ^ 30
fuente