First of all, I want to clarify that this question is different from the questions:
- How to store a 64 bit integer in two 32 bit integers and convert back again
- Is it possible to store 2 32-bit values in one long int variable?
- How to combine two 32-bit integers into one 64-bit integer?
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);
}