Según el estándar C/C++ (ver este enlace), el operador >> en C y C++ no es necesariamente un cambio aritmético para números con signo. Depende de la implementación del compilador si los 0 (lógico) o el bit de signo (aritmético) se desplazan a medida que los bits se desplazan hacia la derecha.
¿Funcionará este código para ASSERT (fallar) en tiempo de compilación para compiladores que implementan un desplazamiento lógico a la derecha para enteros con signo?
#define COMPILE_TIME_ASSERT(EXP) \
typedef int CompileTimeAssertType##__LINE__[(EXP) ? 1 : -1]
#define RIGHT_SHIFT_IS_ARITHMETIC \
( (((signed int)-1)>>1) == ((signed int)-1) )
// SHR must be arithmetic to use this code
COMPILE_TIME_ASSERT( RIGHT_SHIFT_IS_ARITHMETIC );
¡Me parece bien! También puede configurar el compilador para que emita un archivo de ensamblaje (o cargue el programa compilado en el depurador) y ver qué código de operación emite para signed int i; i >> 1;
pero eso no es automático como su solución.
Si alguna vez encuentra un compilador que no implementa el desplazamiento aritmético a la derecha de un número con signo, me gustaría escucharlo.

marton78
¿Por qué afirmar? Si el operador de cambio de su compilador no se ajusta a sus necesidades, puede remediar la situación con gracia extendiendo el resultado con signos. Además, a veces el tiempo de ejecución es lo suficientemente bueno. Después de todo, el optimizador del compilador puede hacer que el tiempo de compilación no sea el tiempo de ejecución:
template <typename Number>
inline Number shift_logical_right(Number value, size_t bits)
{
static const bool shift_is_arithmetic = (Number(-1) >> 1) == Number(-1);
const bool negative = value < 0;
value >>= bits;
if (!shift_is_arithmetic && negative) // sign extend
value |= -(Number(1) << (sizeof(Number) * 8 - bits));
}
Él static const bool
se puede evaluar en tiempo de compilación, por lo que si shift_is_arithmetic
está garantizado para ser true
todo compilador que se precie eliminará todo el if
cláusula y la construcción de const bool negative
como código muerto.
Nota: el código está adaptado de Mono’s encode_sleb128
función: aquí.
Actualizar
Si realmente desea abortar la compilación en máquinas sin cambio aritmético, es mejor que no confíe en el preprocesador. Puedes usar static_assert
(o BOOST_STATIC_ASSERT
):
static_assert((Number(-1) >> 1) == Number(-1), "Arithmetic shift unsupported.");

std”OrgnlDave
De sus diversos comentarios, habla sobre el uso de esta multiplataforma. Asegúrese de que sus compiladores garanticen que cuando compilan para una plataforma, sus operadores de tiempo de compilación se comportarán igual que los de tiempo de ejecución.
Se puede encontrar un ejemplo de comportamiento diferente con números de coma flotante. ¿Su compilador está haciendo sus matemáticas de expresión constante en precisión simple, doble o extendida si está volviendo a int? Como
constexpr int a = 41;
constexpr int b = (a / 7.5);
Lo que digo es que debe asegurarse de que sus compiladores garanticen el mismo comportamiento durante el tiempo de ejecución que el tiempo de compilación cuando trabaja en tantas arquitecturas diferentes.
Es muy posible que un compilador pueda firmar y extender internamente pero no generar los códigos de operación previstos en el objetivo. La única forma de estar seguro es probar en tiempo de ejecución o mirar la salida del ensamblado.
No es el fin del mundo mirar la salida del ensamblaje… ¿Cuántas plataformas diferentes hay? Dado que esto es tan crítico para el rendimiento, simplemente haga el “trabajo” de mirar 1-3 líneas de salida del ensamblador para 5 arquitecturas diferentes. No es como si tuviera que sumergirse en una salida de ensamblaje completa (¡por lo general!) para encontrar su línea. Es muy, muy fácil de hacer.
¿Qué va a hacer su compilación fallida para aquellos que tienen una máquina que usa un cambio lógico? ¿Por qué su software no se podrá utilizar en una máquina/compilador de este tipo? ¿No sería mejor escribir el código para que funcione sin importar si el desplazamiento a la derecha de un número con signo es aritmético o lógico?
–Jonathan Leffler
21 de octubre de 2009 a las 6:03
Estoy usando la selección sin sucursales (BFS) a través del giro de bits. Requiere un cambio aritmético para trabajar. Estoy poniendo COMPILE_TIME_ASSERT (RIGHT_SHIFT_IS_ARITHMETIC); en el encabezado BFS. El código necesita usar la definición RIGHT_SHIFT_IS_ARITHMETIC para seleccionar las rutas tradicionales o sin bifurcaciones. Puede ser una aceleración masiva en las CPU de PS3/XBOX360 usar código libre de bifurcaciones debido a las penalizaciones por errores de predicción de bifurcaciones.
– Adisak
21 de octubre de 2009 a las 15:54
Por cierto, la compilación fallida en una afirmación de tiempo de compilación donde la razón se indica explícitamente es mejor que simplemente hacer que el código falle misteriosamente … básicamente va a decir que estas rutinas no son compatibles con este compilador (o CPU).
– Adisak
21 de octubre de 2009 a las 15:56
Adisak: asegúrese de perfilar la mejora que obtiene de ese truco de agregar máscara: en la unidad int, descubrí que solo a veces es una mejora con respecto al antiguo cmp / bge, dependiendo de qué tan bien el compilador logró intercalar cosas. eso. No fue la victoria masiva absoluta que
fsel
es.– Crashworks
21 de octubre de 2009 a las 17:06
Sugerencia alternativa: si no puede encontrar una buena manera de realizar esta prueba en tiempo de compilación, no hay nada de malo en hacer una prueba rápida en tiempo de ejecución en la parte superior de main() y fallar con un diagnóstico/consejo informativo si el La prueba de tiempo de ejecución no dio los resultados que deseaba. No es tan bueno como un error en tiempo de compilación, pero dado que no se puede pasar por alto en las pruebas, evita la posibilidad de que se envíe accidentalmente con un código incorrecto habilitado.
–Jeremy Friesner
25 de enero de 2019 a las 16:25