преобразование отрицательного числа в потоке c ++

Я столкнулся с проблемой, когда C ++ пытался прочитать текстовый файл, заполненный целыми числами со знаком, в шестнадцатеричной форме и проанализировать их по векторам. Я использовал поток C ++ для перенаправления переменных (stream >> var), и кажется, что отрицательные числа анализируются неправильно — переменная получает значение 0, и устанавливается флаг сбоя потока.

Если я попытаюсь преобразовать строку с помощью функции strtol (), результаты будут такими, как ожидалось. Аналогичным образом, если я попытаюсь сначала перенаправить поток в целое число без знака, а затем преобразовать переменную в целое число со знаком, результаты снова будут правильными, и об ошибке потока не сообщается.

Я использую gcc 6.3.0 в Debian 9.1 (x64), работающем в системе Xeon E5-2643 v3.

Кто-нибудь еще испытывал эту проблему? Я ожидал бы, что преобразование будет работать так же, как функция strtol, и не сообщать об ошибках потока. Я пропускаю некоторые настройки потока / забываю вызвать какую-то функцию или установить какой-либо флаг здесь?

Любые предложения будут ценны.

Ниже приведен пример программы на C ++, демонстрирующей эту проблему.

#include <iostream>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstdint>int main()
{
const char* minus_one = "0xffffffff";

std::stringstream ss;
ss << minus_one;

std::cout << "input string    : " << ss.str() << "\n"; // outputs "0xffffffff"
// C-style conversion
int32_t cint;
cint = strtol(ss.str().c_str(), NULL, 0);
std::cout << "strtol conv     : " << cint <<  " (" << std::hex << cint << ")\n"; // outputs "-1 (ffffffff)"std::cout << std::dec;

// C++-style conversion
int32_t cppint;
ss >> std::hex >> cppint;
std::cout << std::dec << "ssextr conv     : " << cppint <<  " (" << std::hex << cppint << ")\n"; // outputs "0 (0)" <- ERROR
std::cout << std::dec;
if (ss.fail()) std::cout << "Error converting number.\n";

// C++-style conversion with cast
uint32_t cppuint;
int32_t cppint2;
ss.clear();
ss.str(minus_one);
ss >> std::hex >> cppuint;
cppint2 = (int32_t)cppuint;
std::cout << std::dec << "ssextr cast conv: " << cppint2 <<  " (" << std::hex << cppint2 << ")\n"; // outputs "-1 (0xffffffff)"std::cout << std::dec;
if (ss.fail()) std::cout << "Error converting number.\n";

exit(EXIT_SUCCESS);
}

4

Решение

int32_t cint;
cint = strtol(ss.str().c_str(), NULL, 0);

Это читает значение 0xffffffff в long, а затем преобразует это в int32_t, Если long больше 32 бит strtol работает и возвращается 0xffffffff то есть 4294967295, и преобразование этого в int32_t производит -1. Но это не то же самое, что чтение отрицательного числа из строки (и если long 32-битный, тогда он не работает так, как вы ожидаете, вместо этого он возвращает LONG_MAX и преобразует это в int32_t, который 0x7fffffff).

int32_t cppint;
ss >> std::hex >> cppint;

Это пытается прочитать значение 0xffffffff в int32_t но ценность 0xffffffff не вписывается в этот тип, поэтому чтение значения не удается (так же, как это не с strtol когда long является 32-битным).

Ближе к вашему strtol версия будет:

int32_t cppint;
long l;
if (ss >> std::hex >> l)
cppint = l;
else
// handle error ...

Это неразумно ожидать, чтобы иметь возможность прочитать значение 0xffffffff в 32-разрядное целое число со знаком. strtol и istreams не читают битовые шаблоны, они читают числа и число 0xffffffff не вписывается в 32-разрядное целое число со знаком.

1

Другие решения

если первый шестнадцатеричный бит f, тогда c ++ делает его «большим числом»: «0x7fffffff». Кажется, что c ++ не хочет выражать это как отрицательное число.
Как это:

const char* minus_one = "0xf0000000";   //ssextr conv     : 2147483647 (7fffffff)
std::stringstream ss;
ss << minus_one;

// C++ style conversion
int32_t cppint;
ss >> std::hex >> cppint;
std::cout << std::dec << "ssextr conv     : " << cppint <<  " (" << std::hex << cppint << ")\n";
std::cout << std::dec;
if (ss.fail()) {
std::cout << "Error converting number.\n";
}
0

Проблема в том, что шестнадцатеричное обозначение задокументировано для неподписанный целые числа. strtol является функцией C и, по-видимому, является более терпимым к шестнадцатеричному представлению отрицательного целого числа, и внутренне считывает строку как значение без знака, а затем повторно интерпретирует ее как значение со знаком. Но даже в C такая обработка не указана для strtol и для преобразования значения без знака, которое не может быть представлено в типе со знаком либо
результат определяется реализацией или определяется сигнал реализации
. (из черновика 1570 для C11 6.3.1.3 [Преобразования] Целые числа со знаком и без знака)

Вполне вероятно, что это работает таким образом, чтобы не ломать тонны унаследованного кода, но C ++ является более новым языком, и разработчики решили быть более строгими для шестнадцатеричного представления.

0
По вопросам рекламы [email protected]