I will specify the way it works in a more relaxed, mathematically manner, glossing over implementation issues. If you grok that, you should be able to solve the C++ problems on your own.
Let M=2^16, and define, as in C/C++:
uint16 := {0,...,M-1}
int16 := {-M/2,...,M/2-1}
uint32 := {0,...,M^2-1}
int32 := {-M^2/2,...,M^2/2-1}
Now assume you have two uint16 values x,y. You can define a function score_u(x,y) := xM+y.
score_u: uint16xuint16->uint32 is bijective (why?), so you can recover your original values.
Most importantly, you can now add scores modulo M^2 or multiply them by a constant modulo M^2 and then recover the original values at the end and that gives you the same result as if you would have worked with the individual scores modulo M individually, as long as there was never any "overflow" (why?).
And thinking of these numbers as members of residual classes, you are free to replace that number with every other member of his class you prefer at any moment and the result would still be correct up to the residual class.
Now consider the encoding of the score in Stockfish:
Code: Select all
constexpr Score make_score(int mg, int eg) {
return Score((int)((unsigned int)eg << 16) + mg);
}
You must assume, that both operands are from int16, otherwise you get an "overflow". First both operands are converted to their corresponding element in uint32, then score_u is build and finally a conversion to the corresponding int32 is done.
Two's complement must be assumed.
Code: Select all
/// Multiplication of a Score by an integer. We check for overflow in debug mode.
inline Score operator*(Score s, int i) {
Score result = Score(int(s) * i);
//...
return result;
}
The representingint is fetched and multiplied with i. You could now convert that result to uint32 and from there invert score_u to get the individual values.