从signed char转换为unsigned char并返回?
我正在使用JNI,并有一个jbytetypes的数组,其中jbyte表示为一个有符号的字符,即从-128到127.Jbytes表示图像像素。 对于image processing,我们通常希望像素组件的范围从0到255.因此,我想将jbyte值转换为0到255的范围(即与无符号字符相同的范围),对值进行一些计算,然后存储再次结果为jbyte。
我怎样才能安全地做这些转换?
我设法得到这个代码的工作,一个像素值增加30,但钳位值255,但我不明白,如果它是安全的或可移植的:
#define CLAMP255(v) (v > 255 ? 255 : (v < 0 ? 0 : v)) jbyte pixel = ... pixel = CLAMP_255((unsigned char)pixel + 30);
我有兴趣知道如何在C和C ++中做到这一点。
这就是为什么C ++引入了包括static_cast
和reinterpret_cast
的新投射风格的原因之一
有两件事你可以通过说从signed到unsigned的转换,你可能意味着你希望unsignedvariables包含有符号variables的值,以你的无符号types+ 1的最大值为模。就是说,如果你的signed char有一个值为-128,那么CHAR_MAX+1
被添加为128的值,如果值为-1,那么CHAR_MAX+1
被添加为值255,这是由static_cast完成的。 另一方面,可能意味着解释某个variables引用的内存的位值,将其解释为一个无符号字节,而不pipe系统上使用的带符号整数表示,也就是说,如果它具有位值0b10000000
则它应计算为值128位和255位值为0b11111111
,这是通过reinterpret_cast完成的。
现在,对于二进制补码表示,这恰好是完全相同的事情,因为-128被表示为0b10000000
并且-1被表示为0b11111111
并且对于所有这两者之间同样如此。 然而,其他计算机(通常是较老的架构)可能会使用不同的符号表示,如符号和数字或补码。 在补码0b10000000
值不会是-128,而是-127,所以静态转换为无符号字符会使这个129,而reinterpret_cast会使这个128.此外,在补码0b11111111
值不会是-1 ,但是-0(是的,这个值存在于'补码'中),并且会被static_cast转换为值0,而使用reinterpret_cast转换为值255。 注意,在补码的情况下,128的无符号值实际上不能用有符号的字符表示,因为它的值为-07,所以它的范围是从-127到127。
我不得不说,绝大多数计算机将会使用二进制补码,使得整个问题都可以在您的代码永远运行的任何地方进行。 在60年代的时间框架中,你可能只会看到系统中有非常旧的体系结构。
语法归结为以下几点:
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 ++方式来完成这个工作:
jbyte memory_buffer[nr_pixels]; unsigned char* pixels = reinterpret_cast<unsigned char*>(memory_buffer);
或者C的方式:
unsigned char* pixels = (unsigned char*)memory_buffer;
是的,这是安全的。
c语言在执行计算之前使用称为整数提升的特性来增加值中的位数。 因此,你的CLAMP255macros将以整数(可能是32位)精度运行。 结果被分配给一个jbyte,从而将整数精度降低到8位,以适应jbyte。
你是否意识到,当v> 0时,CLAMP255返回0,v <0,255返回?
恕我直言,CLAMP255应该被定义为:
#define CLAMP255(v) (v > 255 ? 255 : (v < 0 ? 0 : v))
差异:如果v不大于255且不小于0:返回v而不是255
有两种方法来解释input数据; -128是最低值,127是最高值(即真正的有符号数据),或者0是最低值,127是中间某处,下一个“高”是-128,-1是“最高”的值(也就是说,最重要的位已经被误解为二进制符号中的符号位)。
假设你的意思是后者,正式的方式是
signed char in = ... unsigned char out = (in < 0)?(in + 256):in;
至lessgcc正确地认为是一个没有操作。
我不是100%确定我理解你的问题,所以告诉我,如果我错了。
如果我知道了,那么你正在阅读技术上有符号字符的jbytes,但实际上像素值从0到255,你想知道如何处理它们而不会破坏这个过程中的值。
那么,你应该做以下几点:
-
在做任何事情之前将jbytes转换为无符号字符,这将定义恢复您尝试操作的像素值
-
使用一个更大的有符号整数types,比如在进行中间计算时使用int,这样可以确保可以检测到并处理溢出和下溢(特别是不会转换为带符号的types,这可能会迫使编译器将每种types都提升为在这种情况下,你将无法检测到下溢)
-
当分配回jbyte时,您需要将您的值限制在0-255范围内,转换为无符号字符,然后再转换为带符号字符:我不确定第一次转换是否必要,但您可以如果你们俩都这样做,那不会是错的
例如:
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);