Каков результат `strtod ( "3ex", & end)` должен быть? Как насчет `sscanf`?

В моих экспериментах это выражение

double d = strtod("3ex", &end);

инициализирует d с помощью 3.0 и помещает end указатель на символ 'e' во входную строку. Это точно так, как я ожидаю, что он будет себя вести. Символ 'e' может выглядеть как начало части экспоненты, но поскольку фактическое значение экспоненты (требуемое в соответствии с 6.4.4.2) отсутствует, то 'e' следует рассматривать как полностью независимый символ.

Однако, когда я делаю

double d;
char c;
sscanf("3ex", "%lf%c", &d, &c);

Я заметил, что sscanf потребляет как '3', так и 'e' для спецификатора формата %lf. Переменная d получает значение 3.0. Переменная c заканчивается 'x'. Это выглядит странно для меня по двум причинам.

Во-первых, поскольку спецификация языка относится к strtod при описании поведения спецификатора формата %f, я интуитивно ожидал, что %lf будет обрабатывать вход таким же образом strtod (т.е. выбрать ту же позицию, что и точка окончания). Тем не менее, я знаю, что исторически scanf должен был возвращать не более одного символа обратно во входной поток. Это ограничивает расстояние любого внешнего вида scanf может выполняться одним символом. И в приведенном выше примере требуется, по крайней мере, два символа вперед. Итак, допустим, я согласен с тем, что %lf потреблял как '3', так и 'e' из входного потока.

Но тогда мы сталкиваемся со второй проблемой. Теперь sscanf должен преобразовать этот "3e" в тип double. "3e" не является допустимым представлением константы с плавающей запятой (опять же, согласно 6.4.4.2 значение экспоненты не является необязательным). Я ожидал бы, что sscanf будет считать этот ввод ошибочным: завершайте при преобразовании %lf, возвращайте 0 и не оставляйте d и c неизмененными. Однако выше sscanf завершается успешно (возврат 2).

Такое поведение согласовано между реализациями GCC и MSVC стандартной библиотеки.

Итак, мой вопрос в том, где именно в стандартном документе на языке C он позволяет sscanf вести себя так, как описано выше, ссылаясь на вышеуказанные две точки: потребление более чем strtod делает и успешно конвертирует такие последовательности как "3e"?

Изучая результаты моего эксперимента, я могу, вероятно, "перестроить" поведение sscanf: потреблять столько, сколько "выглядит правильно", никогда не отступать, а затем просто передавать потребляемую последовательность на strtod. Таким образом, 'e' потребляется %lf, а затем просто игнорируется strtod. Но были ли все это в спецификации языка?

Ответ 1

Я просто нахожу описание ниже на die.net

Функции strtod(), strtof() и strtold() преобразуют начальную часть строки, на которую указывает nptr, чтобы удвоить, поплавать и долго двойное представление, соответственно.

Ожидаемая форма строки (начальная часть) необязательна ведущее белое пространство, как распознано isspace (3), дополнительный плюс ('+') или знак минус ('-'), а затем либо (i) десятичное число, либо (ii) шестнадцатеричное число или (iii) бесконечность или (iv) NAN (Не-число).

Десятичное число состоит из непустой последовательности десятичных цифр возможно содержащий символ радиуса (десятичная точка, зависящий от локали, обычно '.'), необязательно с последующим десятичным показатель. Десятичный показатель состоит из "E" или "e", за которым следует необязательный знак плюс или минус, за которым следует непустая последовательность десятичные цифры и указывает умножение на мощность 10.

Шестнадцатеричное число состоит из "0x" или "0X", за которым следует непустое последовательность шестнадцатеричных цифр, возможно содержащих символ радикса, необязательно с последующим двоичным показателем. Бинарный показатель состоит "P" или "p", за которым следует дополнительный знак "плюс" или "минус", затем непустой последовательностью десятичных цифр и указывает на умножение по мощности 2. По крайней мере один из символов радикса и двоичного показателя должен присутствовать.

Бесконечность - это либо "INF", либо "INFINITY", не считая случая.

A NAN - это "NAN" (без учета случая), за которым необязательно следует '(', a последовательность символов, а затем ')'. Строка символов определяет в зависимости от реализации тип NAN.

Затем я выполнил эксперимент, я выполнил код ниже с помощью gcc

#include <stdlib.h>
#include <stdio.h>

char head[1024], *tail;

void core(const char *stmt){
    sprintf(head, "%s", stmt);
    double d=strtod(head, &tail);
    printf("cover %s to %.2f with length=%ld.\n", head, d, tail-head);
}

int main(){
    core("3.0x");
    core("3e");
    core("3ex");
    core("3e0x");

    return 0;
}

и получить результат

cover 3.0x to 3.00 with length=3.
cover 3e to 3.00 with length=1.
cover 3ex to 3.00 with length=1.
cover 3e0x to 3.00 with length=3.

Итак, Кажется, что за "e" должно быть несколько цифр.

Для sscanf я выполнил еще один эксперимент с кодом gcc:

#include <stdlib.h>
#include <stdio.h>

char head[1024];

void core(const char *stmt){
    int i;sscanf(stmt, "%x%s", &i, head);
    printf("sscanf %s catch %d with '%s'.\n", stmt, i, head);
}

int main(){
    core("0");
    core("0x0g");
    core("0x1g");
    core("0xg");

    return 0;
}

то получим следующий результат:

sscanf 0 catch 0 with ''.
sscanf 0x0g catch 0 with 'g'.
sscanf 0x1g catch 1 with 'g'.
sscanf 0xg catch 0 with 'g'.

Похоже, что sscanf попытается ПОЗВОЛИТЬ БОЛЬШЕ ХАРАКТЕРА И НЕ ДОЛЖЕН РОЛББАК, ЕСЛИ СУДЕБНО, ЧТО ЭТО ОГРАНИЧИВАЕТСЯ В ТЕЧЕНИЕ (МОЖЕТ БЫТЬ НЕЗАКОННО С НЕПОСРЕДСТВЕННОЙ СИТУАЦИЕЙ).