c++ c jni

c++ - ¿Convertir de char firmado a char sin signo y viceversa?



jni (5)

Estoy trabajando con JNI y tengo una matriz de tipo jbyte, donde jbyte se representa como un carácter firmado que va de -128 a 127. Los jbytes representan píxeles de imagen. Para el procesamiento de imágenes, generalmente queremos que los componentes de píxel varíen de 0 a 255. Por lo tanto, quiero convertir el valor de jbyte en el rango 0 a 255 (es decir, el mismo rango que el carácter sin signo), hacer algunos cálculos sobre el valor y luego almacenar el resultado como un jbyte de nuevo.

¿Cómo puedo hacer estas conversiones de forma segura?

Me las arreglé para hacer que este código funcione, donde un valor de píxel se incrementa en 30 pero se fija en el valor 255, pero no entiendo si es seguro o portátil:

#define CLAMP255(v) (v > 255 ? 255 : (v < 0 ? 0 : v)) jbyte pixel = ... pixel = CLAMP_255((unsigned char)pixel + 30);

Me interesa saber cómo hacer esto tanto en C como en C ++.


¿Te das cuenta de que CLAMP255 devuelve 0 para v <0 y 255 para v> = 0?
En mi humilde opinión, CLAMP255 debería definirse como:

#define CLAMP255(v) (v > 255 ? 255 : (v < 0 ? 0 : v))

Diferencia: si v no es mayor que 255 y no menos que 0: devuelve v en lugar de 255


Esta es una de las razones por las cuales C ++ introdujo el nuevo estilo de elenco, que incluye static_cast y reinterpret_cast

Hay dos cosas que puede significar al decir conversión de firmado a no firmado, puede significar que desea que la variable sin signo contenga el valor del módulo de variable con signo el valor máximo de su tipo sin signo + 1. Eso es si su signo firmado tiene un el valor de -128 luego CHAR_MAX+1 se agrega para un valor de 128 y si tiene un valor de -1, entonces se CHAR_MAX+1 para un valor de 255, esto es lo que hace static_cast. Por otro lado, podría querer interpretar el valor de bit de la memoria a la que hace referencia una variable para interpretarse como un byte sin signo, independientemente de la representación de entero con signo utilizada en el sistema, es decir, si tiene un valor de bit 0b10000000 debería evaluar el valor 128 y 255 para el valor de bit 0b11111111 , esto se logra con reinterpret_cast.

Ahora, para la representación del complemento a dos, sucede exactamente lo mismo, ya que -128 se representa como 0b10000000 y -1 se representa como 0b11111111 y lo mismo para todos los demás. Sin embargo, otras computadoras (usualmente arquitecturas antiguas) pueden usar una representación firmada diferente, como la firma y la magnitud o el complemento de uno. En el complemento de uno, el 0b10000000 0b10000000 no sería -128, sino -127, por lo que una conversión estática a char sin signo haría que este 129, mientras que reinterpret_cast lo convertiría en 128. Además en el complemento de uno, el 0b11111111 0b11111111 no sería -1 , pero -0, (sí, este valor existe en el complemento de uno) y se convertiría a un valor de 0 con un static_cast, pero un valor de 255 con un reinterpret_cast. Tenga en cuenta que, en el caso del complemento de uno, el valor sin signo de 128 no se puede representar en un signo con signo, ya que va de -127 a 127, debido al valor -0.

Debo decir que la gran mayoría de las computadoras usarán el complemento de dos, haciendo que todo el tema sea discutible en casi cualquier lugar donde se ejecute el código. Es probable que solo veas sistemas con algo más que el complemento de dos en arquitecturas muy antiguas, piense en el marco temporal de los años 60.

La sintaxis se reduce a lo siguiente:

signed char x = -100; unsigned char y; y = (unsigned char)x; // C static y = *(unsigned char*)(&x); // C reinterpret y = static_cast<unsigned char>(x); // C++ static y = reinterpret_cast<unsigned char&>(x); // C++ reinterpret

Para hacer esto de una manera agradable C ++ con matrices:

jbyte memory_buffer[nr_pixels]; unsigned char* pixels = reinterpret_cast<unsigned char*>(memory_buffer);

o la forma C:

unsigned char* pixels = (unsigned char*)memory_buffer;


Hay dos formas de interpretar los datos de entrada; o bien -128 es el valor más bajo, y 127 es el más alto (es decir, datos firmados verdaderos), o 0 es el valor más bajo, 127 está en algún lugar en el medio, y el siguiente número "superior" es -128, donde -1 es el El valor "más alto" (es decir, el bit más significativo ya se malinterpretó como un bit de signo en una notación de complemento a dos.

Suponiendo que se refiera a lo último, la forma formalmente correcta es

signed char in = ... unsigned char out = (in < 0)?(in + 256):in;

que al menos gcc reconoce correctamente como no operativa.


No estoy 100% seguro de entender tu pregunta, así que dime si me equivoco.

Si lo hice bien, está leyendo jbytes que son caracteres técnicamente firmados, pero realmente valores de píxeles que van de 0 a 255, y se pregunta cómo debería manejarlos sin corromper los valores en el proceso.

Entonces, debes hacer lo siguiente:

  • convierta jbytes en char sin signo antes de hacer cualquier otra cosa, esto restaurará definitivamente los valores de píxel que está tratando de manipular

  • utilice un tipo entero con signo más grande, como int al hacer cálculos intermedios, esto para asegurarse de que los excesos y subdesbordamientos se puedan detectar y solucionar (en particular, no convertir a un tipo firmado podría obligar al compilador a promover cada tipo a un tipo sin signo en cuyo caso no podría detectar subflujos más adelante)

  • cuando asigne de nuevo a un jbyte, querrá ajustar su valor al rango de 0-255, convertir a char sin signo y luego convertir de nuevo a char firmado: no estoy seguro de que la primera conversión sea estrictamente necesaria, pero usted solo puede No estarás equivocado si haces ambas cosas

Por ejemplo:

inline int fromJByte(jbyte pixel) { // cast to unsigned char re-interprets values as 0-255 // cast to int will make intermediate calculations safer return static_cast<int>(static_cast<unsigned char>(pixel)); } inline jbyte fromInt(int pixel) { if(pixel < 0) pixel = 0; if(pixel > 255) pixel = 255; return static_cast<jbyte>(static_cast<unsigned char>(pixel)); } jbyte in = ... int intermediate = fromJByte(in) + 30; jbyte out = fromInt(intermediate);


Si esto es seguro.

El lenguaje c usa una función llamada promoción de enteros para aumentar el número de bits en un valor antes de realizar los cálculos. Por lo tanto, su macro CLAMP255 operará a una precisión entera (probablemente 32 bit). El resultado se asigna a un jbyte, lo que reduce la precisión del entero de vuelta a 8 bits en el jbyte.