web-dev-qa-db-ja.com

符号付き文字から符号なし文字への変換とその逆

私はJNIで作業しており、jbyte型の配列を持っています。jbyteは符号付き文字、つまり-128〜127の範囲で表されます。jbyteは画像のピクセルを表します。画像処理の場合、通常、ピクセルコンポーネントの範囲は0〜255です。したがって、jbyte値を0〜255の範囲(つまり、unsigned charと同じ範囲)に変換し、値に対していくつかの計算を行ってから、再びjbyteとして結果。

これらの変換を安全に行うにはどうすればよいですか?

私はこのコードを機能させることができました。ピクセル値は30ずつ増加しますが、値255に固定されていますが、安全か移植性があるかわかりません:

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

 jbyte pixel = ...
 pixel = CLAMP_255((unsigned char)pixel + 30);

CとC++の両方でこれを行う方法を知りたいです。

56
rbcc

これが、C++が_static_cast_および_reinterpret_cast_を含む新しいキャストスタイルを導入した理由の1つです。

符号付きから符号なしへの変換とは、符号なし変数の値に、符号なし型の最大値+ 1を法とする符号付き変数の値が含まれることを意味する場合があります。値が-128の場合、値128に対して_CHAR_MAX+1_が追加され、値が-1の場合、値255に対して_CHAR_MAX+1_が追加されます。これがstatic_castによって行われます。一方、システムで使用される符号付き整数表現に関係なく、つまり、ビット値が_0b10000000_である場合、変数によって参照されるメモリのビット値を符号なしバイトとして解釈することを意味する場合があります値128、およびビット値_0b11111111_の255に評価する必要があります。これは、reinterpret_castで実現されます。

-128は_0b10000000_として表され、-1は_0b11111111_として表されるため、2つの補数表現ではこれはまったく同じです。ただし、他のコンピューター(通常は古いアーキテクチャー)では、符号と大きさまたは1の補数などの異なる符号付き表現を使用する場合があります。 1の補数では_0b10000000_ビット値は-128ではなく-127であるため、unsigned charへの静的キャストはこの129になり、reinterpret_castでこの128になります。さらに1の補数では_0b11111111_ bitvalueは-1ではなく-0であり(この値は1の補数に存在します)、static_castでは値0に変換されますが、reinterpret_castでは値255に変換されます。 1の補数の場合、128の符号なしの値は、値が-0であるため、-127から127の範囲であるため、実際には符号付き文字で表すことができないことに注意してください。

大部分のコンピューターは2の補数を使用しているため、コードが実行されるほぼすべての場所で問題全体が無駄になります。非常に古いアーキテクチャでは、2の補数以外のシステムのみが表示される可能性があります。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
_

配列を使用してこれをNice C++の方法で行うには:

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

またはCの方法:

_unsigned char* pixels = (unsigned char*)memory_buffer;
_
106
wich

はい、これは安全です。

C言語は、整数プロモーションと呼ばれる機能を使用して、計算を実行する前に値のビット数を増やします。したがって、CLAMP255マクロは整数(おそらく32ビット)の精度で動作します。結果はjbyteに割り当てられ、整数精度がjbyteに収まる8ビットに戻ります。

2
qbert220

CLAMP255はv <0に対して0を返し、v> = 0に対して255を返します。
IMHO、CLAMP255は次のように定義する必要があります。

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

違い:vが255以下で0以上の場合:255の代わりにvを返します

1
Daniel Hilgarth

私はあなたの質問を完全に理解しているわけではないので、間違っているかどうか教えてください。

私がそれを正しければ、あなたは技術的に署名された文字であるjbytesを読んでいますが、実際に 0から255の範囲のピクセル値であり、あなたはそれらをどのように扱うべきか疑問に思っていますプロセスの値を破損することなく。

次に、以下を実行する必要があります。

  • 他の操作を行う前にjbytesをunsigned charに変換すると、操作しようとしているピクセル値が明確に復元されます

  • 中間計算の実行中にintなどの大きな符号付き整数型を使用します。これにより、オーバーフローとアンダーフローを検出して処理できるようになります(特に、notを符号付き型にキャストすると強制的にすべての型を符号なしの型にプロモートするコンパイラ。この場合、後でアンダーフローを検出できなくなります)

  • jbyteに戻す場合、値を0〜255の範囲に固定し、unsigned charに変換してから、signed charに再度変換する必要があります。最初の変換が厳密に必要かどうかはわかりませんが、あなたが両方を行う場合は間違っていません

例えば:

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
ZeRemz

入力データを解釈する方法は2つあります。 -128が最低値、127が最高値(つまり、真の符号付きデータ)、または0が最低値、127が中央のどこかにあり、次の「高い」数値は-128で、-1が「最高」値(つまり、最上位ビットはすでに2の補数表記の符号ビットとして誤って解釈されています。

あなたが後者を意味すると仮定すると、形式的に正しい方法は

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

少なくともgccはノーオペレーションとして適切に認識します。

0
Simon Richter