2009-08-18 7 views
7

que tiene dos cortos de 16 bits (S1 y S2), y yo estoy tratando de combinarlos en un solo número entero de 32 bits (i1). De acuerdo con la especificación que estoy tratando, s1 es la palabra más significativa, y s2 es la palabra menos significativa, y la palabra combinada parece estar firmada. (Es decir, el bit superior de S1 es el signo.)forma más limpia de combinar dos cortos a un int

¿Cuál es la manera más limpia para combinar S1 y S2?

me imaginé algo así como

const utils::int32 i1 = ((s1<<16) | (s2)); 

haría, y parece que funciona, pero estoy preocupado por la izquierda que cambia de un corto por 16.

Además, estoy interesado en el idea de usar un sindicato para hacer el trabajo, cualquier idea sobre si esta es una idea buena o mala?

Respuesta

1

intente proyectar un explicite data.second de tipo corto, como:

const utils::int32 combineddata = ((data.first<<16) | ((short)data.second)); 

edición: Soy C# dev, probablemente la colada en su idioma código es diferente, pero la idea podría ser la misma.

0

quieres lanzar data.first a int32 antes de hacer el cambio, de lo contrario el cambio se desbordará el almacenamiento antes de que tenga la oportunidad de ser promovido de forma automática cuando se asigna a los datos combinados.

Probar:

const utils::int32 combineddata = (static_cast<utils::int32>(data.first) << 16) | data.second; 

Esto es, por supuesto, asumiendo que data.first y data.second son tipos que están garantizados para ser exactamente 16 bits de largo, de lo contrario tendrá problemas mayores.

Realmente no entiendo su afirmación "si data.second se hace demasiado grande, el |. No se tendrá en cuenta el hecho de que los dos son cortos"

Edit: Y Neil tiene toda la razón sobre la firma.

+5

En el caso de los pantalones cortos, no estoy seguro de que sea necesario. El estándar tiene: "Los operandos deben ser de tipo integral o de enumeración y se realizan promociones integrales. El tipo de resultado es el del operando izquierdo promovido". Por lo tanto, el corto se promocionará a un int implícitamente. –

+0

Huh. No lo sabía, creo que es porque siempre soy demasiado paranoico con los tamaños de datos. Gracias por el puntero. –

+0

¡Es bueno ser paranoico porque definitivamente se requeriría el elenco en plataformas con un int de 16 bits! –

13

Lo que está haciendo sólo tiene sentido si los pantalones cortos y la int están sin firmar. Si cualquiera de los cortos está firmado y tiene un valor negativo, la idea de combinarlos en un solo int no tiene sentido, a menos que se le haya proporcionado una especificación específica de dominio para cubrir tal eventualidad.

+0

Acepto que el código publicado tiene un error de extensión de signo, y que la solicitud de "combinar" dos in16_tss con un int32_t es bastante extraña. Pero no creo que sea necesariamente insignificante, en el sentido de que uno podría definir algunas semánticas para la operación. –

+1

¿Has leído las últimas 15 palabras de mi respuesta? –

+0

Sí, parece que la interfaz del hardware está dividiendo un entero con signo en 2 y luego enviándolo, porque tiene un MSW y un LSW, pero aparentemente está en la firma del complemento de 2. Entonces necesito recombinar los datos sensiblemente de eso. – deworde

6

Lo que tienes ve casi correcta, pero probablemente fallará si la segunda parte es negativa; la conversión implícita a int probablemente se extenderá y completará los 16 bits superiores con unos. Un lanzamiento a corto sin firmar probablemente evitaría que eso ocurra, pero la mejor manera de estar seguro es enmascarar los bits.

const utils::int32 combineddata = ((data.first<<16) | ((data.second) & 0xffff)); 
+0

Excelente, eso tiene mucho sentido. – deworde

+0

De hecho, la conversión implícita a int causará problemas. Por ejemplo, 'data.first << 16' también invocará un comportamiento indefinido en este código publicado, si data.first es negativo. Por lo tanto, esta no es una buena solución. En general, no es una buena idea usar operadores de turno junto con números con signo. Mejor solución: 'uint32_t u32 = (uint32_t) data.first << 16 | (uint32_t) data.second; combineddate = (int32_t) u32; ' – Lundin

-1

El uso de un sindicato para hacer el trabajo parece una buena opción, pero es un problema debido a la portabilidad endian diferencias de procesadores. Es factible, pero debe estar preparado para modificar su unión en función de la arquitectura de destino. El cambio de bit es portátil, pero por favor escriba una función/método para hacerlo por usted. En línea si quieres.

En cuanto a la firma de los pantalones cortos, para este tipo de operación, es el significado lo que importa no es el tipo de datos. En otras palabras, si s1 y s2 se deben interpretar como dos mitades de una palabra de 32 bits, tener el bit 15 establecido solo importa si se hace algo que haría que s2 se extendiera.Consulte la respuesta de Mark Ransoms, que podría ser mejor que

inline utils::int32 CombineWord16toLong32(utils::int16 s1, utils::int16 s2) 
{ 
    return ((s1 <<16) | (s2 & 0xffff)); 
} 
3

Dado que nadie lo publicó, así es como se vería la unión. Pero los comentarios sobre endian-ness definitivamente se aplican.

Big-endian:

typedef union { 
    struct { 
     uint16_t high; 
     uint16_t low; 
    } pieces; 
    uint32_t all; 
} splitint_t; 

ascendente hacia la izquierda:

typedef union { 
    struct { 
     uint16_t low; 
     uint16_t high; 
    } pieces; 
    uint32_t all; 
} splitint_t; 
+0

tenga en cuenta que la especificación proporciona cero garantías sobre esto. Es ampliamente usado en C especialmente, y dudo que cualquier compilador lo rompa intencionalmente, pero es un comportamiento estrictamente indefinido. En general, no puede escribir a un miembro de una estructura y leer de otra. – jalf

+0

Tenga en cuenta que la norma no garantiza que 'all' tenga datos razonables después de asignar a' high' y 'low', y viceversa. Esta solución no es portátil. – Juliano

+0

No hay razón para usar una unión para esto, todo lo que logra es problemas de portabilidad. Y el tipo de juego de palabras no está bien definido en C++, a diferencia de C. – Lundin

3

Sé que esto es una entrada antigua, pero la calidad de la actual publicado respuestas es deprimente ...

Estos son los temas a considerar:

  • La promoción de enteros implícitos de los cortos (u otros tipos enteros pequeños) dará como resultado un operando del tipo int que está firmado. Esto sucederá independientemente de la firma del tipo de entero pequeño. La promoción de entero ocurre en las operaciones de turno y en el OR a nivel de bit.
  • En el caso de los operadores de desplazamiento, el tipo resultante es el del operando izquierdo promocionado. En el caso de OR a nivel de bits, el tipo resultante se obtiene de "las conversiones aritméticas habituales".
  • Cambiar a la izquierda un número negativo da como resultado un comportamiento indefinido. Al desplazar hacia la derecha un número negativo, se obtiene un comportamiento definido por la implementación (desplazamiento lógico frente a desplazamiento aritmético). Por lo tanto, los números firmados no se deben usar junto con los cambios de bit en el 99% de todos los casos de uso.
  • Uniones, matrices y similares son soluciones deficientes ya que hacen que el código dependa de la endiosidad. Además, el tipo de juego de palabras a través de uniones tampoco es un comportamiento bien definido en C++ (a diferencia de C). Las soluciones basadas en punteros son malas ya que acabarán violando "la regla de alias estricto".

una solución adecuada por lo tanto:

  • Use operandos con tipos que están garantizados para estar sin firmar y no serán promovidos de manera implícita.
  • Utilice los cambios de bit, ya que son independientes de la endiosidad.

Se verá así:

int32_t i32 = (int32_t)((uint32_t)s1<<16 | (uint32_t)s2); 

Cualquier otra solución es altamente cuestionable y en el mejor de no portátil.

+0

Heh. Para ser honesto, mirando hacia atrás con 8 años más de experiencia, las únicas personas que realmente podrían haber respondido esto son los diseñadores de la API de hardware. Me preguntaba en ese momento si estaban usando un método estándar conocido de combinación, pero ... – deworde

+0

@deworde ¿Qué quiere decir "hardware API"? Este código es lo más parecido posible al metal. – Lundin

Cuestiones relacionadas