ジグザグマッピングについて考える別の方法は、それが符号と大きさの表現にわずかなひねりを加えたものであるということです。
ジグザグマッピングでは、マッピングの最下位ビット(lsb)が値の符号を示します。0の場合、元の値は負ではなく、1の場合、元の値は負です。
負でない値は、lsbの符号ビット用のスペースを確保するために、単純に1ビット左にシフトされます。
負の値の場合、数値の絶対値(大きさ)に対して同じ1ビット左シフトを実行し、lsbに符号を示すだけで済みます。たとえば、-1は0x03または0b00000011にマップできます。ここで、lsbは負であり、1の大きさが1ビットだけ左にシフトされていることを示します。
この符号と大きさの表現の醜い点は、0x01または0b00000001としてマップされた「負のゼロ」です。このゼロの変形は、値の1つを「使い果たし」、表現できる整数の範囲を1つシフトします。おそらく、負のゼロを-2 ^ 63に特殊なケースでマップして、64b2の補数範囲[-2^ 63、2 ^ 63)全体を表すことができるようにします。つまり、貴重なシングルバイトエンコーディングの1つを使用して、小さな大きさの数値用に最適化されたエンコーディングで使用されることはほとんどない値を表し、特殊なケースを導入しました。これは悪いことです。
これは、この符号と大きさの表現でジグザグのねじれが発生する場所です。符号ビットはまだlsbにありますが、負の数の場合、特別なケーシングの負のゼロではなく、大きさから1を減算します。ここで、-1は0x01にマップされ、-2 ^ 63も特殊なケースでは表現されません(つまり、-マグニチュード2 ^ 63 -1、左に1ビットシフト、lsb /符号ビットが設定され、すべてのビットが1に設定されます) 。
したがって、ジグザグエンコーディングについて考える別の方法は、よりスマートな符号と大きさの表現であるということです。符号ビットはlsbに格納され、負の数の大きさから1が減算され、大きさは1ビット左にシフトされます。
符号を明示的にテストするよりも、投稿した無条件のビット単位の演算子を使用してこれらの変換を実装する方が高速です。特殊なケースでは、負の値を操作し(たとえば、1を否定して減算するか、ビット単位ではない)、大きさをシフトしてから、明示的に設定します。 lsb符号ビット。ただし、これらは実質的に同等であり、このより明確な符号と大きさの一連のステップは、これらのことを何を、なぜ行っているのかを理解するのが簡単な場合があります。
C / C ++のビットシフトの負の値は移植性がないため、避ける必要がありますが、警告します。負の値を左にシフトすると未定義の動作が発生し、負の値を右にシフトすると実装で定義された動作が発生します。正の整数を左シフトしても、未定義の動作が発生する可能性があります(たとえば、符号ビットにシフトすると、トラップなどが発生する可能性があります)。したがって、一般に、C /C++では符号付き型をビットシフトしないでください。"いやだっていうだけだよ。"
最初に型の署名されていないバージョンにキャストして、標準に従って安全で明確な結果を取得します。これは、負の値の算術シフトがなく、論理シフトのみが発生することを意味します。したがって、それを考慮してロジックを調整する必要があります。
Cの64b整数のジグザグマッピングの安全でポータブルなバージョンは次のとおりです(算術否定に注意してください)。
#include <stdint.h>
uint64_t zz_map( int64_t x )
{
return ( ( uint64_t ) x << 1 ) ^ -( ( uint64_t ) x >> 63 );
}
int64_t zz_unmap( uint64_t y )
{
return ( int64_t ) ( ( y >> 1 ) ^ -( y & 0x1 ) );
}