¿Verificar que el desplazamiento a la derecha firmado por C/C++ es aritmético para un compilador en particular?

7 minutos de lectura

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 );

  • ¿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

¡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.

  • Sí, básicamente lo quiero automático… mirar los códigos de operación no es una expectativa razonable para este requisito porque lo estoy usando en una biblioteca que otros equipos pueden usar en varias plataformas.

    – Adisak

    20 oct 2009 a las 22:50

  • El compilador del sistema UNISYS 2200 utiliza el desplazamiento lógico a la derecha para los tipos con signo. “El resultado de la expresión E1 >> E2 es que E1 (interpretado como un patrón de bits) se desplaza a las posiciones de bits E2 correctas. El desplazamiento a la derecha es lógico (es decir, cero a la izquierda) incluso si la expresión E1 es un tipo entero con signo.” public.support.unisys.com/2200/docs/cp14.0/pdf/78310422-011.pdf

    – phuclv

    17 de agosto de 2015 a las 5:29

  • Desplazamiento a la derecha firmado: qué compilador usa el desplazamiento lógico

    – phuclv

    25 de enero de 2019 a las 16:19

avatar de usuario
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 truetodo 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.");

  • Hay una gran cantidad de código (por ejemplo, Branch-Free-Selection usando máscaras) que solo tiene sentido para usar en compiladores con desplazamiento aritmético con signo. La emulación elegante de un cambio aritmético en ellos probablemente sería significativamente más lenta que el código original, por lo tanto, eliminando la “optimización”.

    – Adisak

    17/09/2012 a las 15:55


  • Lo suficientemente justo. Sin embargo, mi segundo punto sigue siendo válido: no confíes en el preprocesador. Ver actualización.

    – marton78

    17 de septiembre de 2012 a las 16:48

  • static_assert() es C++11x y no podemos usar boost. Pero la verificación que estaba haciendo no depende del preprocesador, solo podría hacer lo siguiente, pero es mucho más difícil de leer y comprender que el ejemplo que escribí: typedef int CompileTimeAssertArithmeticShift[( (((signed int)-1)>>1) == ((signed int)-1) ) ? 1 : -1];

    – Adisak

    17/09/2012 a las 22:44

avatar de usuario
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.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad