Ошибка в работе сложения bitset
#include <iostream>
#include <bitset>
#include <cstdio>
template <size_t N>
std::bitset<N> sum (const std::bitset<N>& b1, const std::bitset<N>& b2) {
std::bitset<N> result;
bool carry = false;
for (size_t i = 0 ; i < N ; ++i) {
unsigned value = b1[i] + b2[i] + (carry ? 1 : 0);
carry = value > 1;
result[i] = value % 2;
printf ("[%d] %u %u -> %d (%d)\n", (int)i, b1[i], b2[i], result[i], (int)carry);
}
return result;
}
int main () {
std::bitset<10> first(20);
std::bitset<10> second (15);
std::cout << sum (first, second).to_ulong() << std::endl;
}
Неправильно работает побитовое сложение bitset. Как исправить или какие еще есть варианты сложения двоичных чисел? 1 скрин ошибки и вывод. 2 скрин что должно выводить
Ответы (2 шт):
constexpr bool operator[]( std::size_t pos ) const; reference operator[]( std::size_t pos );printf ("[%d] %u %u -> %d (%d)\n", (int)i, b1[i], b2[i], result[i], (int)carry);
Через %u и %d выводится то, что не является (unsigned) int'ом. А именно, некий reference.
printf ("[%d] %u %u -> %u (%d)\n", (int)i, (unsigned)b1[i], (unsigned)b2[i], (unsigned)result[i], (int)carry);
[0] 0 1 -> 1 (0)
[1] 0 1 -> 1 (0)
[2] 1 1 -> 0 (1)
[3] 0 1 -> 0 (1)
[4] 1 0 -> 0 (1)
[5] 0 0 -> 1 (0)
[6] 0 0 -> 0 (0)
[7] 0 0 -> 0 (0)
[8] 0 0 -> 0 (0)
[9] 0 0 -> 0 (0)
35
И вообще, я бы код записал покрасивее: https://ideone.com/BX2PS9
template <size_t N> std::bitset<N> sum (const std::bitset<N>& b1, const std::bitset<N>& b2) {
std::bitset<N> result;
unsigned value = 0;
for (size_t i = 0; i < N; ++i) {
value += b1[i] + b2[i];
result[i] = value & 1;
value >>= 1;
printf ("[%z] %u %u -> %u (%u)\n", i, (unsigned)b1[i], (unsigned)b2[i], (unsigned)result[i], value);
}
return result;
}
Кстати, можно ещё немного поиграться с форматом и присобачить %hhu - это формат для числового вывода unsigned char, так что я пока не знаю, разрешено ли стандартом его применять к bool, но они оба однобайтовые, и всё работает: https://ideone.com/kLoP8L
printf ("[%z] %hhu %hhu -> %hhu (%u)\n", i, b1[i], b2[i], result[i], value);
%hhu не работает - выведен мусор.
PS: А ещё есть сомнительное место, но может быть так и задумано: перенос из последнего разряда. По идее результатам может быть std::bitset<N+1>.
Шаблонный класс std::bitset имеет две версии оператора operator[] — константную и неконстантную версии:
constexpr bool operator[]( std::size_t pos ) const;
reference operator[]( std::size_t pos );
где reference — это вспомогательный классовый тип, предназначенный для установки отдельных битов у объектов типа std::bitset.
В приведённом коде
printf ("[%d] %u %u -> %d (%d)\n", (int)i, b1[i], b2[i], result[i], (int)carry);
имеют место несоответствия между спецификаторами преобразования и типами фактически передаваемых в функцию printf аргументов, что ведёт к неопределённому поведению.
Выражения b1[i] и b2[i] возвращают значения типа bool (используется константная версия operator[]), которые перед передачей в функцию printf преобразуются в тип int (см.: Вывод bool в printf), однако, используются спецификаторы преобразования %u, которые говорят, что аргументы должны иметь тип unsigned int, следовательно поведение программы не определено.
Выражение result[i] возвращает значение классового типа std::bitset<N>::reference (используется неконстантная версия operator[]), но используемый спецификатор преобразования %d говорит, что аргумент должен иметь тип int, следовательно поведение программы не определено.
N1570, 7.21.6.1/9:
[...] If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
Так как класс std::bitset<N>::reference имеет оператор преобразования в тип bool, то проблемную строку можно переписать так:
std::printf("[%d] %d %d -> %d (%d)\n", (int)i, b1[i], b2[i], (bool)result[i], carry);
[0] 0 1 -> 1 (0)
[1] 0 1 -> 1 (0)
[2] 1 1 -> 0 (1)
[3] 0 1 -> 0 (1)
[4] 1 0 -> 0 (1)
[5] 0 0 -> 1 (0)
[6] 0 0 -> 0 (0)
[7] 0 0 -> 0 (0)
[8] 0 0 -> 0 (0)
[9] 0 0 -> 0 (0)
35

