Преобразование без знака в stringstream нарушено? - PullRequest
4 голосов
/ 24 апреля 2009

Рассмотрим эту программу:

#include <iostream>
#include <string>
#include <sstream>
#include <cassert>

int main()
{
    std::istringstream stream( "-1" );
    unsigned short n = 0;
    stream >> n;
    assert( stream.fail() && n == 0 );
    std::cout << "can't convert -1 to unsigned short" << std::endl;
    return 0;
}

Я пробовал это на gcc (версия 4.0.1 Apple Inc., сборка 5490) на OS X 10.5.6, и утверждение верно; он не может преобразовать -1 в беззнаковый шорт.

Однако в Visual Studio 2005 (и 2008) утверждение не выполняется, и результирующее значение n совпадает с ожидаемым при неявном преобразовании, сгенерированном компилятором, то есть «-1» равно 65535, «-2» равно 65534 и т. Д. Но тогда это становится странным в "-32769", который преобразуется в 32767.

Кто прав, а кто здесь не прав? (И что, черт возьми, происходит с -32769 ??)

Ответы [ 3 ]

5 голосов
/ 25 апреля 2009

Поведение, заявленное GCC в посте Макса Либберта, основано на таблицах стандарта C ++, которые отображают поведение iostream на преобразователи printf / scanf (или, по крайней мере, это мое чтение). Однако поведение scanf в g ++ отличается от поведения istream:

#include <iostream>
#include <cstdio>
using namespace std;;

int main()
{
    unsigned short n = 0;
    if ( ! sscanf( "-1", "%hu", &n ) ) {
        cout << "conversion failed\n";
    }
    else {
        cout << n << endl;
    }
}

на самом деле печатает 65535.

3 голосов
/ 25 апреля 2009

Во-первых, чтение строки «-1» как отрицательного числа зависит от локали (для локали было бы возможным идентифицировать отрицательные числа, заключив их в круглые скобки). Ваш стандарт по умолчанию - "классический" язык C :

Безусловно, доминирующее использование locales неявно в потоковом вводе / выводе. Каждый istream и ostream имеет свою собственную локаль . языковой стандарт потока по умолчанию является глобальным языковым стандартом на момент создания потока (стр. 6). ...

Первоначально глобальной локалью является стандартная локаль C, locale :: classic () (стр. 11).

По словам ребят из GCC, числовое переполнение допускает сбой операции ввода потока (речь идет об отрицательных числах, которые переполнили целое число со знаком):

[T] поведение libstdc ++ - v3 строго соответствует стандарту. ... При попытке чтения оно не помещается в подписанное int i и не удается.

Благодаря другому ответу была обнаружена ошибка, и это поведение изменилось :

Упс, очевидно, мы никогда не анализировали правильно отрицательные значения для unsigned. исправить это просто. ...

Исправлено в основной линии, будет исправлено и в 4.4.1.

Во-вторых, хотя целочисленное переполнение обычно предсказуемо, я считаю, что это официально неопределенное поведение , поэтому, хотя я не могу сказать, почему -32769 "конвертируется в 32767, я думаю, что это разрешено.

1 голос
/ 24 апреля 2009

Попробуйте этот код:

#include <iostream>
#include <string>
#include <sstream>
#include <cassert>

int main()
{
    std::istringstream stream( "-1" );
    std::cout << "flags: " << (unsigned long)stream.flags() << std::endl;
    return 0;
}

Я попробовал это на моем VS2005:

flags: 513

и на codepad.org (который, я думаю, использует g ++) это дает:

flags: 4098

Это говорит мне о том, что gcc использует другое значение по умолчанию fmtflags. Поскольку fmtflags контролирует, какие преобразования возможны, вы получаете разные результаты.

...