How to store and use two 32-bit signed int in one 64-bit int?

1

First of all, I want to clarify that this question is different from the questions:

That this question is store and use, which mean I can do this

int64_t score = make_score(-15, 15);
score += make_score(-5, 5); //I can use (add, subtract) the score
int32_t a = get_a(score);
assert(a == -20); //-15 -5 = -20
int32_t b = get_b(score);
assert(b == 20);//15 + 5= 20

This is achievable for two 16-bit int in one 32-bit int (Stockfish did this):

/// Score enum stores a middlegame and an endgame value in a single integer (enum).
/// The least significant 16 bits are used to store the middlegame value and the
/// upper 16 bits are used to store the endgame value. We have to take care to
/// avoid left-shifting a signed int to avoid undefined behavior.
enum Score : int { SCORE_ZERO };

constexpr Score make_score(int mg, int eg) {
  return Score((int)((unsigned int)eg << 16) + mg);
}

/// Extracting the signed lower and upper 16 bits is not so trivial because
/// according to the standard a simple cast to short is implementation defined
/// and so is a right shift of a signed integer.
inline Value eg_value(Score s) {
  union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s + 0x8000) >> 16) };
  return Value(eg.s);
}

inline Value mg_value(Score s) {
  union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s)) };
  return Value(mg.s);
}

I'm trying to upgrade mg and eg from int16_t to int32_t but I can't figure out how to do it, I always have trouble when ScoreA + ScoreB ruin the eg and mg inside the Score.

Here is what I tried and failed:

enum Score : int64_t { SCORE_ZERO };

constexpr Score make_score(int mg, int eg) {
  return Score((int)((uint64_t)eg << 32) + mg);
}

inline Value eg_value(Score s) {
  union { uint32_t u; int32_t s; } eg = { uint32_t(unsigned(s + 0x80000000) >> 32) };
  return Value(eg.s);
}

inline Value mg_value(Score s) {
  union { uint32_t u; int32_t s; } mg = { uint32_t(unsigned(s)) };
  return Value(mg.s);
}
c++
data-structures
int
bit-manipulation
asked on Stack Overflow Jul 10, 2020 by ChessLover

5 Answers

6

Use memcpy.

As the comment in the original solution pointed out, this kind of bit manipulations are a minefield of potential undefined behavior. memcpy allows you to get rid of those and is well understood by modern compilers, so it will still result in efficient machine code.

enum Score : int64_t { SCORE_ZERO };

enum Value : int32_t { FORTYTWO };

inline Score make_score(int32_t mg, int32_t eg) {
    int64_t combined;
    std::memcpy(&combined, &eg, 4);
    std::memcpy(reinterpret_cast<char*>(&combined) + 4, &mg, 4);
    return Score(combined);
}

inline Value eg_value(Score s) {
    int32_t eg;
    std::memcpy(&eg, &s, 4);
    return Value(eg);
}

inline Value mg_value(Score s) {
    int32_t mg;
    std::memcpy(&mg, reinterpret_cast<char*>(&s) + 4, 4);
    return Value(mg);
}

Try it on godbolt.

answered on Stack Overflow Jul 10, 2020 by ComicSansMS • edited Jul 10, 2020 by ComicSansMS
1

The problem is that you still have some "int" and "unsigned" keywords left that still convert into the 32 bit version. So replace each "int" with "int64_t" and each "unsigned" with "uint64_t" and it should work as expected.

answered on Stack Overflow Jul 10, 2020 by koalo
0

This can be different approach for this question

#include<iostream>
#include<cstdint>
#include<bitset>
using namespace std;
int main()
{
    bitset<32> bit32[2] ={ 45 ,-415152545 };
    bitset<64> bit64;
    
    // store in 64 bit varibale
    int index=0;
    int position=0;
    for(int i=0;i<64;i++)
    {
       bit64[i] =bit32[index][i-position];
       if(i==31)
         {   index = 1; position=32; }
    }
    
    // reset 32 bit container ,index and position 
       bit32[2] ={0};
       index=0;
       position=0;
    
    
   // fetching data in 32 bit container from 64 bit and assign it into a and b .
     int32_t a;
     int32_t b;
    for(int i=0;i<64;i++)
    {
       bit32[index][i-position] = bit64[i];
       if(i==31)
         {   index = 1; position=32; }
    }
    
  
    a = bit32[0].to_ulong();
    b = bit32[1].to_ulong();
    cout<<a<<" "<<b;
 
}
answered on Stack Overflow Jul 10, 2020 by Ramesh Choudhary
0

You could use union as well:

#include <stdint.h>
#include <iostream>


union Score {
    int64_t    u64;
    int32_t    u32[2];

    Score() : u64(0) {}
    Score(int64_t v) : u64(v) {}
    Score(int32_t a, int32_t b): u32{a, b} {}

    Score & operator=(Score const & original) { if(&original != this) { u64 = original.u64; } return *this; }

    int32_t get_a() {return u32[0];}
    int32_t get_b() {return u32[1];}
    int64_t get() {return u64;}

    Score operator+(Score const & other) {
            return Score(u32[0] + other.u32[0], u32[1] + other.u32[1]);
        }

    Score & operator+=(Score const & other) {
            u32[0] += other.u32[0];
            u32[1] += other.u32[1];
            return *this;
        }
};


int main() {

    Score v(-15, 15);

    std::cout << "The size is: " << sizeof(Score) << " Bytes" << std::endl;
    std::cout << "A is: " << v.get_a() << std::endl;
    std::cout << "B is: " << v.get_b() << std::endl;

    std::cout << "adding -5, +5" << std::endl;
    Score v1 = v + Score(-5, 5);
    std::cout << "A is: " << v1.get_a() << std::endl;
    std::cout << "B is: " << v1.get_b() << std::endl;

    std::cout << "adding -10, +10" << std::endl;
    v += Score(-10, 10);
    std::cout << "A is: " << v.get_a() << std::endl;
    std::cout << "B is: " << v.get_b() << std::endl;


    return 0;
}

Output:

The size is: 8 Bytes
A is: -15
B is: 15
adding -5, +5
A is: -20
B is: 20
adding -10, +10
A is: -25
B is: 25
answered on Stack Overflow Jul 10, 2020 by PirklW
0

It's easy.

int64_t value;
int32_t* value1 = (int32_t*)&value;
int32_t* value2 = (int32_t*)&value + 1;

Example:

#include <cstdint>


int main() {
    int64_t value;
    int32_t* value1 = (int32_t*)&value;
    int32_t* value2 = (int32_t*)&value + 1;

    *value1 = 10; // Example
    *value2 = 20;

    return 0;
}
answered on Stack Overflow Nov 27, 2020 by Ihor Drachuk

User contributions licensed under CC BY-SA 3.0