tl; dr: ((n + 9)/10) * 10
compila al mejor código (más rápido) de asm en más casos, y es fácil de leer y entender para las personas que saben lo que hace la división de enteros en C. Es un modismo bastante común.
No he investigado cuál es la mejor opción para algo que necesita funcionar con n
negativo, ya que es posible que desee redondear desde cero, en lugar de seguir hacia + Infinito, dependiendo de la aplicación.
En cuanto a las operaciones de C utilizados por las diferentes sugerencias, la luz-peso mayor es Mark Dickinson (en los comentarios):
(n+9) - ((n+9)%10)
Se parece más eficiente que la brecha directo/multiplican sugerido por un par de personas (incluyendo @bta): ((n + 9)/10) * 10
, porque solo tiene un complemento en lugar de un multiplicador. (n+9
es una subexpresión común que sólo tiene que ser calculada una vez).
Resulta que ambos compilar a código literalmente idénticos, usando el truco compilador de convertir division by a constant en un multiply and shift, see this Q&A for how it works. A diferencia de una instrucción de hardware div
que cuesta lo mismo ya sea que use el cociente, el resto o ambos resultados, el método mul/shift toma pasos adicionales para obtener el resto. Entonces el compilador ve que puede obtener el mismo resultado de un cálculo más barato y termina compilando ambas funciones con el mismo código.
Esto es cierto en x86, ppc, and ARM, y todas las otras arquitecturas que he visto en el explorador del compilador Godbolt. En la primera versión de esta respuesta, vi un sdiv
para el %10
en Godbolt's gcc4.8 para ARM64, pero ya no está instalado (¿quizás porque estaba mal configurado?) ARM64 gcc5.4 no hace eso.
Godbolt tiene MSVC (CL) instalado ahora, y algunas de estas funciones se compilan de forma diferente, pero no me he tomado el tiempo para ver cuál compila mejor.
Tenga en cuenta que en la salida de gcc para x86, se multiplica por 10 se hace barato con lea eax, [rdx + rdx*4]
hacer n * 5, a continuación, add eax,eax
duplicar esa. imul eax, edx, 10
tendría una latencia de 1 ciclo más en Intel Haswell, pero sería más corta (una menos uop). gcc/sonido metálico no lo uso incluso con -Os -mtune=haswell
:/
la respuesta aceptada (n + 10 - n % 10
) es incluso más barato para calcular: n+10
puede ocurrir en paralelo con n%10
, por lo que la cadena de dependencias es un paso más corto. Compila una instrucción menos.
Sin embargo, da la respuesta incorrecta para múltiplos de 10: p. Ej. 10 -> 20
. La solución sugerida usa un if(n%10)
para decidir si hacer algo. Esto se compila en un cmov
, por lo que es más largo y peor que el código de @ Bta. Si vas a usar un condicional, hazlo para obtener resultados sanos para las entradas negativas.
Así es como se comportan todas las respuestas sugeridas, incluyendo para las entradas negativas:
./a.out | awk -v fmt='\t%4s' '{ for(i=1;i<=NF;i++){ a[i]=a[i] sprintf(fmt, $i); } } END { for (i in a) print a[i]; }'
i -22 -21 -20 -19 -18 -12 -11 -10 -9 -8 -2 -1 0 1 2 8 9 10 11 12 18 19 20 21 22
mark -10 -10 -10 -10 0 0 0 0 0 0 0 0 0 10 10 10 10 10 20 20 20 20 20 30 30
igna -10 -10 -10 0 0 0 0 0 10 10 10 10 10 10 10 10 10 20 20 20 20 20 30 30 30
utaal -20 -20 -20 -10 -10 -10 -10 -10 0 0 0 0 0 10 10 10 10 10 20 20 20 20 20 30 30
bta -10 -10 -10 -10 0 0 0 0 0 10 10 10 10 10 10 10 10 10 20 20 20 20 20 30 30
klatchko -10 -10 -10 -10 0 0 0 0 0 0 0 0 0 10 10 10 10 10 20 20 20 20 20 30 30
branch -10 -10 -20 0 0 0 0 -10 10 10 10 10 0 10 10 10 10 10 20 20 20 20 20 30 30
(transpose awk program)
n + (((9 - (n % 10)) + 1) % 10)
obras de Ignacio "correctamente" para enteros negativos, el redondeo hacia + Infinity , pero es mucho más costoso de computar. Requiere dos operaciones de módulo, por lo que es esencialmente el doble de costoso. Compila aproximadamente el doble de instrucciones x86, haciendo aproximadamente el doble del trabajo de las otras expresiones.
programa Resultado de impresión (igual que los enlaces Godbolt anteriores)
#include <stdio.h>
#include <stdlib.h>
int f_mark(int n) { return (n+9) - ((n+9)%10); } // good
int f_bta(int n) { return ((n + 9)/10) * 10; } // compiles to literally identical code
int f_klatchko(int n) { return n + 10 - n % 10; } // wrong, needs a branch to avoid changing multiples of 10
int f_ignacio(int n) { return n + (((9 - (n % 10)) + 1) % 10); } // slow, but works for negative
int roundup10_utaal(int n) { return ((n - 1)/10 + 1) * 10; }
int f_branch(int n) { if (n % 10) n += (10 - n % 10); return n; } // gcc uses cmov after f_accepted code
int main(int argc, char**argv)
{
puts("i\tmark\tigna\tutaal\tbta\tklatch\tbranch");
for (int i=-25 ; i<25 ; i++)
if (abs(i%10) <= 2 || 10 - abs(i%10) <= 2) // only sample near interesting points
printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\n", i, f_mark(i), f_accepted(i),
f_ignacio(i), roundup10_utaal(i), f_bta(i), f_branch(i));
}
para aclarar algo que varias personas han comentado, tenga en cuenta que el dominio de esta función está limitada a los números enteros 0-150. – bta
No entiendo, ¿qué pasa con esta simple pregunta? ¿Es realmente un juego de ingenio? –
@ralu: Las preguntas más simples siempre reciben los votos por aquí, porque muy pocas personas miran las difíciles. –