Unterschiedliche Rechenergebnisse: Debug und Release



  • Alles arbeitet nach unserem Ermessen fehlerfrei. Aber der Microsoft Visual Studio 2010 Professional Compiler mit der Einstellung x64 Release Ob1 oder Ob 2 kompiliert fehlerhaft. Die Ausgabe in der Console muss am Ende heissen:

    value........50100
    temp.........50100
    

    Hier der gesamte und stark vereinfachte Code (kürzer geht's kaum). Am besten rauskopieren und unter Visual Studio 2010 selbst staunen.

    #include <stdio.h>
    
    typedef unsigned __int64 bitboard_t;  // long long
    
    const int IS_ENPASSANT = 1 << 15;
    
    struct move_info_c {
      int move;
      int value;
    };
    
    struct board_c {
      int color_on[64];
      int piece_on[64];
      bitboard_t piece_bb[6];
      bitboard_t color_bb[2];
    };
    
    class sort_c {
    public:
      sort_c();
      move_info_c * last_move;
      move_info_c move_list[256];
    };
    
    static bitboard_t pawn_attack_bb[2][64];
    static board_c B;
    
    static void initialize_fen_position(const char * position, const char * to_move, const char * castle, const char * enpassant);
    static move_info_c * generate_evasions(move_info_c * move_list);
    static int static_exchange_evaluation(int move);
    static void initialize_bitboards();
    
    inline int move_to(int move) {
      return move & 63;
    }
    
    inline int move_from(int move) {
      return (move >> 6) & 63;
    }
    
    inline int move_is_enpassant(int move) {
      return (move & IS_ENPASSANT) != 0;
    }
    
    inline int make_enpassant(int from, int to) {
      return to | (from << 6) | IS_ENPASSANT;
    }
    
    inline int square_to_file(int square) {
      return square & 7;
    }
    
    inline int square_to_rank(int square) {
      return square >> 3;
    }
    
    inline int file_and_rank_to_square(int file, int rank) {
      return file + (rank << 3);
    }
    
    inline int piece_value_midgame(int piece) {
      int piece_value_mg[7] = {100, 300, 400, 500, 900, 1000, 0};
      return piece_value_mg[piece];
    }
    
    inline bitboard_t file_and_rank_to_bitboard(int file, int rank) {
      return static_cast<bitboard_t>(1) << static_cast<bitboard_t>(file_and_rank_to_square(file, rank));
    }
    
    inline bitboard_t pawn_attack_bitboard(int color, int square) {
      return pawn_attack_bb[color][square];
    }
    
    inline int color_on_square(int square) {
      return B.color_on[square];
    }
    
    inline int piece_on_square(int square) {
      return B.piece_on[square];
    }
    
    inline bitboard_t pawn_bitboard(int color) {
      return B.piece_bb[0] & B.color_bb[color];
    }
    
    inline int move_is_capture(int move) {
      return (piece_on_square(move_to(move)) != 6) || move_is_enpassant(move);
    }
    
    inline int move_piece(int move) {
      return piece_on_square(move_from(move));
    }
    
    inline int move_piece_captured(int move) {
      return move_is_enpassant(move) ? 0 : piece_on_square(move_to(move));
    }
    
    void initialize_bitboards() {
      for (int square = 0; square < 64; square++) {
        int file = square_to_file(square);
        int rank = square_to_rank(square);
    
        pawn_attack_bb[0][square] = 0;
        pawn_attack_bb[1][square] = 0;
    
        if (file - 1 >= 0 && rank + 1 <= 7) pawn_attack_bb[0][square] |= file_and_rank_to_bitboard(file - 1, rank + 1);
        if (file + 1 <= 7 && rank + 1 <= 7) pawn_attack_bb[0][square] |= file_and_rank_to_bitboard(file + 1, rank + 1);
        if (file - 1 >= 0 && rank - 1 >= 0) pawn_attack_bb[1][square] |= file_and_rank_to_bitboard(file - 1, rank - 1);
        if (file + 1 <= 7 && rank - 1 >= 0) pawn_attack_bb[1][square] |= file_and_rank_to_bitboard(file + 1, rank - 1);
      }
    }
    
    void initialize_fen_position() {
      for (int square = 0; square < 64; square++) {
        B.color_on[square] = 2;
        B.piece_on[square] = 6;
      }
      B.color_on[28] = 1;
      B.piece_on[28] = 0;
      B.color_on[29] = 0;
      B.piece_on[29] = 0;
    }
    
    move_info_c * generate_evasions(move_info_c * move_list) {
      (move_list++)->move = make_enpassant(28, 21);
      return move_list;
    }
    
    int static_exchange_evaluation(int move) {
      int from = move_from(move);
      int to = move_to(move);
      int me = color_on_square(from);
      int you = me ^ 1;
      int piece = piece_on_square(from);
      int piece_captured = move_piece_captured(move);
    
      if (   (pawn_attack_bitboard(me, to) & pawn_bitboard(you))
          && (piece_value_midgame(piece) > piece_value_midgame(piece_captured))
          && (piece_value_midgame(piece) > 100)) {
        return piece_value_midgame(piece_captured) - piece_value_midgame(piece);
      }
      return 0;
    }
    
    sort_c::sort_c() {
      int temp = 0;
    
      last_move = generate_evasions(move_list);
    
      move_info_c * current = move_list;
    
      int move = current->move;
      int value = static_exchange_evaluation(move);
    
      if (value < 0) {
        current->value = value;
      }
      else if (move_is_capture(move)) {
        temp = 50000 + piece_value_midgame(move_piece_captured(move)) - move_piece(move);
        current->value = temp;
      }
      else {
        current->value = 0;
      }
    
      printf_s("value...........%d\n", current->value);
      printf_s("temp............%d\n", temp);
    }
    
    int main() {
      initialize_bitboards();
      initialize_fen_position();
      sort_c s;
      return 0;
    }
    


  • 32 bit funktioniert mit release bei mir



  • also schrieb:

    32 bit funktioniert mit release bei mir

    Nur mit 64-Bit Release und den optimierenden Flags Ob1 und Ob2 unter Visual Studio 2010 Professional gibts Probleme mit folgender fehlerhaften Consolenausgabe:

    value...........50000 
    temp............50000
    


  • Bin nicht so fit mit mehrdimensionalen rohen Arrays bei C++, aber könnte es sein, dass es vielleicht

    static bitboard_t pawn_attack_bb[64][2];
    

    statt

    static bitboard_t pawn_attack_bb[2][64];
    

    heißen muss?

    Edit: Obwohl das ja eigentlich äquivalent sein müsste..



  • life schrieb:

    Bin nicht so fit mit mehrdimensionalen rohen Arrays bei C++, aber könnte es sein, dass es vielleicht

    static bitboard_t pawn_attack_bb[64][2];
    

    statt

    static bitboard_t pawn_attack_bb[2][64];
    

    heißen muss?

    Edit: Obwohl das ja eigentlich äquivalent sein müsste..

    Nein, das ist schon korrekt und bedeutet pawn_attack[color][square], also weiss oder schwarz und a1 bis h8.

    Habe das Phänomen im CCC Forum gepostet. Dort tummeln sich Schachexperten die auch von Assembler und Compilern Ahnung haben. Bisher geht das Feedback in Richtung Compiler-Bug. Aber einen Beweis gibt es noch nicht.

    Wie sieht es denn mit folgendem Code aus?

    inline int piece_value_midgame(int piece) {
      //assert(piece_is_ok(piece)); 
      static const int piece_value_mg[7] = {100, 300, 400, 500, 900, 1000, 0}; 
      return piece_value_mg[piece]; 
    }
    

    Ist das sprachlich korrekt eine Look-up-table als static und const in eine Inline Function zu packen? Die Look-up-table wird sonst nirgendwo benötigt und ist in der Function gut aufgehoben (sonst müsste sie extern sein, da die Function normalerweise in einer Header steht). Dadurch kann ich zur Laufzeit den Wert von piece bequem überprüfen und die table kapseln. Allerdings verändert sich das Verhalten des Programms, wenn ich static und const wegnehme oder lasse? Obwohl das doch erstmal egal sein sollte?

    Gruss



  • "inline" ist hauptsächlich nur ein Hinweis für den Compiler, dass es lohnenswert sein könnte die Funktion zu inlinen. Wenn sich der Compiler tatsächlich dazu entschließt die Funktion zu inlinen, darf das (beobachtbare) Verhalten dabei nicht verändert werden.

    Ansonsten ist mir nur noch das aufgefallen:

    return square >> 3
    

    Wobei square ein int ist. Das Ergebnis ist implementation-defined falls square negativ ist.

    Ich kanns aber leider bei mir nicht testen, da ich kein 64bit System zur Hand habe ;).



  • Im Microsoft Forum wird von einem Compiler-Fehler ausgegangen. Auch im Computerschach Forum spekuliert man darüber. Aber eine konkrete Aussage, von Jemandem, der Ahnung von Compiler-Flags und Assembler hat, war bisher noch nicht zu bekommen.

    Bis dahin muss ich hoffen, dass Jemand mit mehr Kenntnissen als ich, eine plausible Erklärung liefern kann 😕



  • Schreib doch einfach mal nen offiziellen Bugreport dazu an Microsoft und schau, was sie sagen. Mit etwas Glück bekommste dann auch einen Hotfix für den Bug (sofern es wirklich ein Compilerbug war).



  • MS untersucht das Problem und vielleicht wissen wir in einigen Tagen bescheid.



  • Tomahawk schrieb:

    MS untersucht das Problem und vielleicht wissen wir in einigen Tagen bescheid.

    wär cool, wenn du dann noch mal bescheid geben würdest, was raus kam - mich interessiert es zumindest... ^^
    zu mal der bug im msvc10 ja neu hinzugekommen sein muss, weil die 9 es noch richtig macht... ^^

    bb


Anmelden zum Antworten