1
votes

Comment stocker et utiliser deux entiers signés 32 bits dans un int 64 bits?

Tout d'abord, je tiens à préciser que cette question est différente des questions:

Que cette question est de stocker et d'utiliser , ce qui signifie que je peux le faire

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

Ceci est réalisable pour deux 16- bit int dans un int 32 bits ( Stockfish l'a fait ):

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

J'essaye de mettre à jour mg et eg à partir de int16_t à int32_t mais je ne sais pas comment le faire, j'ai toujours des problèmes lorsque ScoreA + ScoreB ruine le code eg et mg > dans le score.

Voici ce que j'ai essayé et échoué:

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


4 commentaires

Si vous devez emballer ensemble dans int32_t , pourquoi n'utilisez-vous pas une classe ou une struct ? Et pourquoi abusez-vous des énumérations en tant que nombres entiers? Score devrait mieux être un alias pour int64_t plutôt qu'une énumération.


Dans le prolongement du commentaire précédent, où et comment utilisez-vous score comme int64_t et pourquoi ne pourriez-vous pas utiliser une classe , là?


@churill: J'utiliserai certainement la classe pour Score (et je n'utilise pas les enums comme des entiers, les développeurs de Stockfish l'ont fait: D), mais j'essaie de mettre à jour l'ancien code, je migrer vers la classe va générer plus de problèmes pour moi :RÉ


PS: déprimé, j'ai décidé de migrer Score vers struct pour me sauver de la folie du bit, étrangement, j'ai réussi! se trouve migrer vers struct est plus facile.


5 Réponses :


1
votes

Le problème est qu'il vous reste encore des mots clés "int" et "unsigned" qui sont encore convertis dans la version 32 bits. Remplacez donc chaque "int" par "int64_t" et chaque "unsigned" par "uint64_t" et cela devrait fonctionner comme prévu.


0 commentaires

6
votes

Utilisez memcpy . Comme l'a souligné le commentaire de la solution originale, ce type de manipulation de bits est un champ de mines de comportements potentiels indéfinis. memcpy vous permet de vous en débarrasser et est bien compris par les compilateurs modernes, il en résultera donc toujours un code machine efficace.

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

Essayez-le sur godbolt .


3 commentaires

La bonne réponse à mon humble avis. Quelques suggestions: make_score devrait également être en ligne, sinon l'éditeur de liens se plaindra des symboles en double. Aussi pourquoi les reinterpret_cast ? Les paramètres de memcpy sont void * et chaque adresse y est implicitement convertible. Enfin, au lieu de coder en dur magique 4 , je recommanderais l'utilisation de sizeof .


Il y a un bug: mg_value ((Score) (make_score (0,0) -make_score (15,15))) devrait retourner -15 , mais il renvoie -16


@Resurrection Vous avez besoin de la distribution ici pour obtenir le décalage au milieu de int64_t . Avec la valeur d'origine du pointeur Score * , vous ne pouvez accéder qu'aux adresses alignées sur sizeof (Score) . Bien sûr, convertir en void * à la place fonctionnerait également, mais de nombreux compilateurs mettent en garde contre l'arithmétique du pointeur sur void * . Bonne prise sur le inline manquant.



0
votes

Cette approche peut être différente pour cette 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;
 
}


0 commentaires

0
votes

Vous pouvez également utiliser l'union:

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

Sortie:

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


0 commentaires

0
votes

C'est facile.

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

Exemple:

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


0 commentaires