Skip to content

[libc++] mersenne_twister_engine compares equal after a stream round trip but produces a different sequence #242467

Open

Description

@0z5a

Since #222700, a std::mt19937 that is written with operator<< and read back with operator>> compares equal to the original but produces a different sequence, unless the original had produced a multiple of 624 values.

#include <iostream>
#include <random>
#include <sstream>
int main() {
  std::mt19937 a(7), b;
  (void)a(); // any number of draws that is not a multiple of 624
  std::stringstream s; s << a; s >> b;
  std::cout << "a == b: " << (a == b) << '\n';
  int i = 0; while (i < 1000 && a() == b()) ++i;
  std::cout << "first differing output: " << (i < 1000 ? i : -1) << '\n';
}

clang++ -std=c++17 -stdlib=libc++ -O1, libc++ trunk as of 2026-11-13 (_LIBCPP_VERSION 240000, Compiler Explorer x86-64 clang trunk):

a == b: 1
first differing output: 623

libc++ from LLVM 23.1, libstdc++ and the MSVC STL print first differing output: -1. After k draws the read-back engine differs at output 624 - k % 624 (k = 5: 619, k = 1000: 248).

x == y means the two engines have the same state ([rand.req.eng]), and engines with the same state produce the same sequence, so a == b cannot be true here. The written representation also changed: [rand.eng.mers]/5 says it consists of X(i-n), ..., X(i-1), so for std::mt19937 g(7); std::cout << g; the first value is the seed. Trunk prints 2022286329 1444425037 ...; LLVM 23.1, libstdc++ and the MSVC STL print 7 4097098180 ....

Since #222700 the state array holds values that are already twisted. operator<< writes that array starting at __i_, and operator>> stores it back with __i_ = 0. After 624 - k outputs the restored engine returns again the k values that the original returned before it was written, and then twists a rotated array. Programs that save and restore an engine with << and >> get a different sequence after the restore. release/23.x does not contain e364b9d, so this is in trunk only.

I don't have a fix: writing X(i-n), ..., X(i-1) from the twisted storage needs a decision on how the state is kept.

Assisted-by: Claude (claude-opus-5-5). I reviewed this text.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    libc++libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions