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);
}
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);
}
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.
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;
}
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
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;
}
User contributions licensed under CC BY-SA 3.0