ChessLover
ChessLover

Reputation: 11

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

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);
}

Upvotes: 1

Views: 1123

Answers (5)

Ihor Drachuk
Ihor Drachuk

Reputation: 1293

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;
}

Upvotes: 0

ComicSansMS
ComicSansMS

Reputation: 54609

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.

Upvotes: 6

PirklW
PirklW

Reputation: 484

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

Upvotes: 0

Ramesh Choudhary
Ramesh Choudhary

Reputation: 426

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;
 
}

Upvotes: 0

koalo
koalo

Reputation: 2313

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.

Upvotes: 1

Related Questions