Convert from signed char to unsigned char and vice versa?

I work with JNI and have an array of type jbyte where jbyte is represented as a signed char ie from -128 to 127. jbytes represent the pixels of the image. For image processing, we usually want the pixel components to be in the range from 0 to 255. Therefore, I want to convert the jbyte value to a range from 0 to 255 (that is, in the same range as the unsigned char), perform some calculations on the value and then save the result again as jbyte.

How can I make this conversion safe?

I managed to get this code to work, where the pixel value is incremented by 30, but bound to a value of 255, but I don’t understand if it is safe or portable:

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

I am interested to know how to do this in both C and C ++.

+43
c ++ c jni
Feb 18 '11 at 11:47
source share
5 answers

This is one of the reasons why C ++ introduced a new casting style that includes static_cast and reinterpret_cast

There are two things that you can say by saying a conversion from signed to unsigned, you can mean that you want the unsigned variable to contain the value of the signed variable modulo the maximum value of your unsigned type + 1. That is, if your signed char has a value -128, then CHAR_MAX+1 added for value 128, and if it has a value of -1, then CHAR_MAX+1 added for value 255, this is what static_cast is doing. On the other hand, you can interpret the value of a memory bit referenced by any variable that should be interpreted as an unsigned byte, regardless of the signed integer representation used in the system, that is, if it has a value of bit 0b10000000 , it should evaluate to values ​​128 and 255 for bit value 0b11111111 , this is done using reinterpret_cast.

Now for two complementary representations, this happens in exactly the same way, since -128 is represented as 0b10000000 , and -1 is represented as 0b11111111 and similarly for all in between. However, other computers (usually older architectures) may use a different signed representation, such as sign and magnitude or addition. In addition to this, bit 0b10000000 will not be -128, but -127, so static casting to unsigned char will make it 129, while reinterpret_cast will make it 128. In addition, in addition to them, 0b11111111 bitvalue will not be -1 , but -0, (yes, this value exists in their complement) and will be converted to a value of 0 with static_cast, but a value of 255 with reinterpret_cast. Please note that in case of addition to them, the unsigned value of 128 cannot actually be represented in the signed char, since it ranges from -127 to 127, due to the value of -0.

I have to say that the vast majority of computers will use two add-ons, which will solve the whole problem almost anywhere in your code. You will probably only ever see systems with something other than two additions in very old architectures, think about the timeframes of the 60s.

The syntax is as follows:

 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 

To do this with a good C ++ way with arrays:

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

or method C:

 unsigned char* pixels = (unsigned char*)memory_buffer; 
+87
Feb 18 2018-11-18T00:
source share

Yes, it is safe.

In c, a function called integer promotion is used to increase the number of bits in a value before performing calculations. Therefore, your CLAMP255 macro will work with integer (possibly 32-bit) precision. The result is assigned to jbyte, which reduces the whole precision to 8 bits that fit into jbyte.

+2
Feb 18 '11 at 11:51
source share

Do you understand that CLAMP255 returns 0 for v <0 and 255 for v> = 0?
IMHO, CLAMP255 should be defined as:

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

Difference: if v is not greater than 255 and not less than 0: return v instead of 255

+1
Feb 18 '11 at 11:55
source share

There are two ways to interpret the input; either -128 is the lowest value, and 127 is the highest (i.e. true signed data), or 0 is the lowest value, 127 is somewhere in the middle, and the next "highest" number is -128, with -1 is the "highest" value (i.e., the most significant bit has already been misinterpreted as a sign bit in a two-digit notation.

Assuming you mean the last, formally correct way

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

which at least gcc correctly recognizes as no-op.

0
Feb 18 '11 at 12:08
source share

I am not 100% sure that I understand your question, so tell me if I am wrong.

If I understand correctly, you are reading jbytes, which are technically signed characters, but really pixel values ​​are from 0 to 255, and you are wondering how you should handle them without distorting the values ​​in this process.

Then you should do the following:

  • convert jbytes to unsigned char before doing anything else, this will certainly lead to restoring the pixel values ​​that you are trying to manipulate

  • use a larger integer character type, such as int, when doing intermediate calculations to make sure that over- and underflows can be detected and processed (in particular, not casting to a signed type can lead to compilation to advance each type to an unsigned type, and in this case, you will not be able to detect defects later)

  • when assigning back to jbyte you will need to pin your value in the range 0-255, convert to unsigned char and then convert to signed char again: I'm not sure that the first conversion is strictly necessary, but you just can’t be wrong if you both perform

For example:

 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); 
0
Feb 18 2018-11-18T00:
source share



All Articles