Как получить доступ к диапазону бит в битах?
У меня есть набор битов, который очень большой, скажем, 10 миллиардов бит.
Что я хотел бы сделать, это записать это в файл. Однако используя .to_string()
на самом деле зависает мой компьютер.
То, что я хотел бы сделать, это перебрать биты и взять 64 бита за раз, превратить его в uint64
а затем записать его в файл.
Однако я не знаю, как получить доступ к различным диапазонам набора битов. Как бы я это сделал? Я новичок в C++ и не был уверен, как получить доступ к лежащему в основе bitset::reference, поэтому приведите пример ответа.
Я попытался использовать указатель, но не получил то, что ожидал. Вот пример того, что я пытаюсь до сих пор.
#include <iostream>
#include <bitset>
#include <cstring>
using namespace std;
int main()
{
bitset<50> bit_array(302332342342342323);
cout<<bit_array << "\n";
bitset<50>* p;
p = &bit_array;
p++;
int some_int;
memcpy(&some_int, p , 2);
cout << &bit_array << "\n";
cout << &p << "\n";
cout << some_int << "\n";
return 0;
}
выход
10000110011010100111011101011011010101011010110011
0x7ffe8aa2b090
0x7ffe8aa2b098
17736
Кажется, последнее число меняется при каждом запуске, что не соответствует ожиданиям.
2 ответа
В программе есть пара ошибок. Максимальное значение bitset<50>
может держать это 1125899906842623
и это намного меньше чем что bit_array
был инициализирован с помощью в программе.
some_int
должен быть определен как unsigned long
и проверьте, если unsigned long
имеет 64 бита на вашей платформе.
После этого проверьте каждый бит bit_array
в цикле, а затем выполните соответствующие побитовые операции (ИЛИ и сдвиг) и сохраните результат в some_int
,
std::size_t start_bit = 0;
std::size_t end_bit = 64;
for (std::size_t i = start_bit; i < end_bit; i++) {
if (bit_array[i])
some_int |= mask;
mask <<= 1;
}
Вы можете изменить значения start_bit
а также end_bit
соответственно, когда вы перемещаетесь по большому набору битов.
Смотрите ДЕМО.
Для доступа к диапазонам bitset
, вы должны посмотреть на предоставленный интерфейс. Отсутствие чего-то вроде bitset::data()
означает, что вы не должны пытаться получить доступ к основным данным напрямую. Делать это, даже если это казалось работающим, хрупкое, хакерское и, возможно, какое-то неопределенное поведение.
Я вижу две возможности для преобразования массивного bitset
на более управляемые части. Довольно простой подход состоит в том, чтобы просто проходить побитно и собирать их в какое-то целое число (или записывать их непосредственно в файл как '0'
или же '1'
если вас не беспокоит размер файла). Похоже, PW уже предоставил код для этого, поэтому я пока пропущу пример.
Вторая возможность заключается в использовании побитовых операторов и to_ullong()
, Недостатком этого подхода является то, что он номинально использует вспомогательное пространство памяти, а именно два дополнительных набора битов того же размера, что и ваш оригинал. Я говорю "номинально", потому что компилятор может быть достаточно умен, чтобы оптимизировать их. Может быть. Возможно, нет. И вы имеете дело с размерами более гигабайта каждый. Реально, побитовый подход, вероятно, является подходящим вариантом, но я думаю, что этот пример интересен на теоретическом уровне.
#include <iostream>
#include <iomanip>
#include <bitset>
#include <cstdint>
using namespace std;
constexpr size_t FULL_SIZE = 120; // Some large number
constexpr size_t CHUNK_SIZE = 64; // Currently the mask assumes 64. Otherwise, this code just
// assumes CHUNK_SIZE is nonzero and at most the number of
// bits in long long (which is at least 64).
int main()
{
// Generate some large bitset. This is just test data, so don't read too much into this.
bitset<FULL_SIZE> bit_array(302332342342342323);
bit_array |= bit_array << (FULL_SIZE/2);
cout << "Source: " << bit_array << "\n";
// The mask avoids overflow in to_ullong().
// The mask should be have exactly its CHUNK_SIZE low-order bits set.
// As long as we're dealing with 64-bit chunks, there's a handy constant to handle this.
constexpr bitset<FULL_SIZE> mask64(UINT64_MAX);
cout << "Mask: " << mask64 << "\n";
// Extract chunks.
const size_t num_chunks = (FULL_SIZE + CHUNK_SIZE - 1)/CHUNK_SIZE; // Round up.
for ( size_t i = 0; i < num_chunks; ++i ) {
// Extract the next CHUNK_SIZE bits, then convert to an integer.
const bitset<FULL_SIZE> chunk_set{(bit_array >> (CHUNK_SIZE * i)) & mask64};
unsigned long long chunk_val = chunk_set.to_ullong();
// NOTE: as long as CHUNK_SIZE <= 64, chunk_val can be converted safely to the desired uint64_t.
cout << "Chunk " << dec << i << ": 0x" << hex << setfill('0') << setw(16) << chunk_val << "\n";
}
return 0;
}
Выход:
Source: 010000110010000110011010100111011101011011010101011010110011010000110010000110011010100111011101011011010101011010110011
Mask: 000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111
Chunk 0: 0x343219a9dd6d56b3
Chunk 1: 0x0043219a9dd6d56b