Converting from signed char to unsigned char and back again?
我正在使用JNI并且有一个jbyte类型的数组,其中jbyte表示为有符号的char,即-128到127.jbytes表示图像像素。 对于图像处理,我们通常希望像素分量的范围为0到255.因此我想将jbyte值转换为0到255的范围(即与unsigned char相同的范围),对值进行一些计算然后存储 结果再次作为jbyte。
如何安全地进行这些转换?
我设法让这段代码工作,其中一个像素值增加30但是被限制为255,但我不明白它是安全的还是可移植的:
1 2 3 4 | #define CLAMP255(v) (v > 255 ? 255 : (v < 0 ? 0 : v)) jbyte pixel = ... pixel = CLAMP_255((unsigned char)pixel + 30); |
我很想知道如何在C和C ++中做到这一点。
这是C ++引入新的强制转换样式的原因之一,其中包括
你可以通过说明从有符号到无符号的转换来表示两件事,你可能意味着你希望无符号变量包含有符号变量的值,模数为无符号类型的最大值+ 1.这就是你的签名字符有一个如果值为-128,则为
现在,对于二进制补码表示,这恰好是完全相同的事情,因为-128表示为
我不得不说绝大多数计算机都会使用两个补码,这使得整个问题几乎无处不在。你可能只会在非常古老的架构中看到除了两个补码以外的系统,想想60年代的时间框架。
语法归结为以下内容:
1 2 3 4 5 6 7 | 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 |
要用一个很好的C ++方式用数组做到这一点:
1 2 | jbyte memory_buffer[nr_pixels]; unsigned char* pixels = reinterpret_cast<unsigned char*>(memory_buffer); |
或C方式:
1 | unsigned char* pixels = (unsigned char*)memory_buffer; |
是的,这是安全的。
c语言使用称为整数提升的功能来在执行计算之前增加值中的位数。因此,您的CLAMP255宏将以整数(可能是32位)精度运行。结果被分配给jbyte,这会将整数精度降低到适合jbyte的8位。
你是否意识到,对于v <0,CLAMP255返回0,对于v> = 0,CLAMP255返回255?
恕我直言,CLAMP255应定义为:
1 | #define CLAMP255(v) (v > 255 ? 255 : (v < 0 ? 0 : v)) |
差异:如果v不大于255且不小于0:返回v而不是255
我不是百分百肯定我理解你的问题,所以告诉我,如果我错了。
如果我做对了,你正在读取技术上签名的字符jbytes,但实际上像素值的范围是0到255,你想知道如何处理它们而不破坏过程中的值。
然后,您应该执行以下操作:
-
在执行任何其他操作之前将jbytes转换为unsigned char,这将绝对恢复您尝试操作的像素值
-
在进行中间计算时使用较大的有符号整数类型(例如int),以确保可以检测和处理上溢和下溢(特别是,不转换为有符号类型可能会强制编译器将每种类型提升为无符号类型,在这种情况下,您将无法在以后检测到下溢)
-
当分配回jbyte时,你需要将你的值钳位到0-255范围,转换为unsigned char然后再转换为signed char:我不确定第一次转换是否是必要的,但你可以如果你们两个都做错了,那就错了
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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); |
有两种方法可以解释输入数据; -128是最低值,127是最高值(即真正的有符号数据),或0是最低值,127是中间的某个地方,下一个"更高"数字是-128,其中-1是"最高"值(也就是说,最重要的位已被误解为二进制补码表示法中的符号位。
假设你的意思是后者,那么正式的方法是
1 2 | signed char in = ... unsigned char out = (in < 0)?(in + 256):in; |
至少gcc正确地认为是无操作。