Signed int会破坏较大位域的一部分

时间:2017-01-09 04:43:57

标签: c++ bit-manipulation

在尝试为我的游戏制作网格列表的空间网格时,我试图根据特定网格的坐标数据制作唯一的哈希。我尝试获取网格数据:WorldName world_name; uint8_t stage_index; short int pos_x; short int pos_y; short int pos_z;并尝试构建一个唯一的uint64_t hash_bitfield

当网格坐标为正时,我的uint64_t GenerateHash(...)效果很好。但是,当任何都是负数时,我会产生不良影响:

world: 1        -->> 0000 0001
stage: 1        -->> 0000 0001
pos_x: 23       -->> 0000 0000 0001 0111
pos_y: -23      -->> 1111 1111 1110 1001
pos_z: 23       -->> 0000 0000 0001 0111

[ world ] [ stage ] [      pos_x      ] [      pos_y      ] [      pos_z      ]
--byte7-- --byte6-- --byte5-- --byte4-- --byte3-- --byte2-- --byte1-- --byte0--
1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1110 1001 0000 0000 0001 0111 <-- what I get
0000 0001 0000 0001 0000 0000 0001 0111 0000 0000 0001 0111 0000 0000 0001 0111 <-- when y is positive

What I expected:                        vvvvvvvvvvvvvvvvvvv
0000 0001 0000 0001 0000 0000 0001 0111 1111 1111 1110 1001 0000 0000 0001 0111

请注意,pos_y为负数会在该字节的每隔一位翻转为1。当所有数字都是正数时,就不会发生这种事。正如您在下面的代码中所看到的,我尝试在将位复制到uint64_t(注释块)后手动翻转符号位。它也没用。

#include <iostream>
#include <string>
#include <sstream>
#include <bitset>
using namespace std;

enum WorldName{    // These are collections of gameobj sectors
    WORLD_TYPE1,
    WORLD_TYPE2,
    WORLD_TYPE3,
    MAX_WORLDS
};

uint64_t GenerateHash( WorldName world_name, uint8_t stage_index, short int pos_x, short int pos_y, short int pos_z ){
    uint64_t world_name_t = world_name;
    uint64_t stage_index_t = stage_index;
    uint64_t pos_x_t = pos_x;
    uint64_t pos_y_t = pos_y;
    uint64_t pos_z_t = pos_z;

    // Try to flip the sign manually (it doesn't help)
    //uint64_t pos_x_t, pos_y_t, pos_z_t;
    //if( pos_x < 0 ){ pos_x_t = ~(uint64_t( abs( pos_x ) )) + 1; } else { pos_x_t = pos_x; }
    //if( pos_y < 0 ){ pos_y_t = ~(uint64_t( abs( pos_y ) )) + 1; } else { pos_y_t = pos_y; }
    //if( pos_z < 0 ){ pos_z_t = ~(uint64_t( abs( pos_z ) )) + 1; } else { pos_z_t = pos_z; }

    uint64_t hash_bitfield = 0x0;
    hash_bitfield |= world_name_t << (8 * 7);
    hash_bitfield |= stage_index_t << (8 * 6);
    hash_bitfield |= pos_x_t << (8 * 4);
    hash_bitfield |= pos_y_t << (8 * 2);
    hash_bitfield |= pos_z_t;

    return hash_bitfield;
}

void PrintBitString( std::string bitstr, std::string label="", bool print_ruler=true ){
    std::string buffer;
    int count = 0;
    for( size_t i = 0; i < bitstr.size(); i++ ){
        buffer += bitstr[i];
        count++;
        if( count >3 ){ buffer += ' ';  count = 0; }
    }

    if( print_ruler ){
        int num_of_bytes_to_draw = (bitstr.size() / 8);
        for( size_t k = num_of_bytes_to_draw; k > 0; --k ){
            cout << "--byte" << k - 1 << "-- ";
        }
        cout << endl;
    }

    cout << buffer << endl;
}

int main() {
    WorldName world_name = (WorldName) 1;    // 4B
    uint8_t stage_index = 1;     // 1B
    short int pos_x = 23;        // 2B
    short int pos_y = -23;       // 2B
    short int pos_z = 23;        // 2B

    std::bitset<8> world_name_bits( world_name );
    std::bitset<8> stage_index_bits( stage_index );
    std::bitset<16> pos_x_bits( pos_x );
    std::bitset<16> pos_y_bits( pos_y );
    std::bitset<16> pos_z_bits( pos_z );
    cout << "world: " << world_name << "\t-->> " << world_name_bits << endl;
    cout << "stage: " << (int)stage_index << "\t-->> " << stage_index_bits << endl;
    cout << "pos_x: " << pos_x << "\t-->> " << pos_x_bits << endl;
    cout << "pos_y: " << pos_y << "\t-->> " << pos_y_bits << endl;
    cout << "pos_z: " << pos_z << "\t-->> " << pos_z_bits << endl << endl;

    uint64_t my_hash = GenerateHash( world_name, stage_index, pos_x, pos_y, pos_z );
    uint64_t my_hash_abs = GenerateHash( world_name, stage_index, pos_x, abs(pos_y), pos_z );

    cout << "[ world ] [ stage ] [      pos_x      ] [      pos_y      ] [      pos_z      ]" << endl;
    std::bitset<64> bits( my_hash );
    std::bitset<64> bits_abs( my_hash_abs );
    PrintBitString( bits.to_string() );
    PrintBitString( bits_abs.to_string(),"",false );
    cout << endl;

    system("PAUSE");
}

如何获得一个看起来像我的[期望]位域的uint64_t(其中pos_y的位域仅影响byte2和byte3的16位)?谢谢你的帮助!

1 个答案:

答案 0 :(得分:0)

您可以将pos_x等变量转换为unsigned short,然后再将其分配给pos_x_t

uint_64_t pos_x_t = (unsigned short) pos_x;

或者您可以屏蔽值

uint_64_t pos_x_t = uint_64_t(pos_x) & 0xFFFF;

或者在hash_bitfield

中存储时使用适当的遮罩
hash_bitfield |= (pos_x_t & 0xFFFF) << (8 * 4);

遮挡也可以在换挡后进行,但是你需要使用遮挡的移位版本。