Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Как я могу проверить, может ли строка быть преобразована в число с плавающей запятой?

Автор вопроса: LotharОтветов: 7

Во-первых, мой контекст - это контекст автора компилятора, которому нужно преобразовать литералы с плавающей запятой (строки) в значения с плавающей запятой/двойные числа. Я не занимался программированием с плавающей запятой последние 15 лет, поэтому я почти уверен, что это совершенно глупый вопрос новичка.

double res;
errno=0;
*res = strtod((const char*)literal,NULL);
if (errno==ERANGE) throw_a_literal_out_of_range_exception();
return res;

но в библиотеке времени выполнения c нет функции "strtof"?

РЕДАКТИРОВАТЬ: Чтобы уточнить. Я уже знаю, что строка 'literal' является допустимым шаблоном для числа C с плавающей запятой. Он уже прошел проверку регулярными выражениями. Я просто хочу проверить, есть ли проблема с диапазоном/точностью.

Причина в исходном коде Eiffel, который пользователь может написать

a := {REAL_32} 3.1415
b := {REAL_32} 3.0E200

Чтобы преобразовать записанное число с плавающей запятой в 32-битное число с плавающей запятой. Думаю, во втором случае компилятор должен определить, что значение выходит за пределы допустимого диапазона, и выдать ошибку или хотя бы предупреждение.

  • Почему вам нужно, чтобы это было число с плавающей точкой, а не двойное? 20.12.2009
  • Я думаю, вы должны преобразовать свой литерал в двойной. Затем приступайте к актерскому составу. Во время каста вы должны проверить, находится ли двойное значение в пределах диапазона с плавающей запятой. 21.12.2009

ctype-conversionfloating-point

Ответы

Ответ 1 · рейтинг 2 · Alok Singhal

Поскольку у вас есть значение в double, вы можете просто проверить, выходит ли оно за пределы диапазона float:

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

int main(int argc, char *argv[])
{
    double d = FLT_MAX;
    if (argc > 1) {
        d = strtod(argv[1], NULL);
    }
    if ((d > 0 && (d > FLT_MAX || d < FLT_MIN))
                || (d < 0 && (d < -FLT_MAX || d > -FLT_MIN)))
        printf("Invalid float: %g\n", d);
    else
        printf("Valid: %g\n", d);

    return EXIT_SUCCESS;
}

Запуск программы:

$ ./a.out
Valid: 3.40282e+38
$ ./a.out 3.5e38
Invalid float: 3.5e+38
$ ./a.out -1e40
Invalid float: -1e+40

Вам может понадобиться или не понадобиться добавить тест для правильного возврата strtod(), в зависимости от того, есть ли возможность переполнения в типе double.

Ответ 2 · рейтинг 9 · Community

В C89 вы можете использовать sscanf.

Например:

float myfloat;

if(sscanf(str, "%f", &myfloat) != 1)
    /* String could not be converted */
else
    /* String was converted, myfloat is usable */

Ответ 3 · рейтинг 6 · jamesdlin

@ Николас Гой: я не думаю, что sscanf(str, "%f, &float) == 1 (или != 1) действительно делает то, что нужно.

Если в str есть дополнительные символы (например, "1.1foo"), он будет парсить без ошибок, что, вероятно, нежелательно. Это можно исправить, выполнив:

char dummy;
if (sscanf(str, "%f%c", &float, &dummy) != 1) {
   /* Parse error. */
} else {
   /* Parsed cleanly. */
}

вместо. Тем не менее, sscanf с %f, скорее всего, будет использовать strtod внутренне и все равно будет приведено к float. Язык в стандарте C говорит:

a,e,f,g Соответствует необязательному знаковому числу с плавающей запятой, бесконечности или NaN, формат которого совпадает с ожидаемым для последовательности субъектов функции strtod. Соответствующий аргумент должен быть указателем на плавающий.

что подразумевает это, и это кажется правдой для меня (gcc 4.2.1 на FreeBSD). Используя приведенный выше код sscanf, "1.79e308" анализируется успешно, но имеет значение +inf, как и "5e-300" со значением 0.0, и это те же результаты, которые вы получили бы от (float) 1.79e308 и (float) 5e-300.

Так или иначе. Все сказанное, я задаюсь вопросом, почему OP все равно хочет использовать float вместо double.

Ответ 4 · рейтинг 5 · Community

Я хотел сказать, просто используйте код, который у вас уже есть, но присвойте результат strod() веществу с плавающей запятой, а не двойному. Но ваш код неверен по нескольким причинам.

Во-первых, вы не можете проверить errno, если не произошла ошибка. Во-вторых, strtod() не будет устанавливать errno, за исключением таких вещей, как ошибки диапазона. Если вы передадите ему недопустимый номер, например "XYZ", он не будет установлен.

Более правильное использование strtod:

char *p;
double d = strtod( "123.4xyz", & p );
if ( * p != 0 ) {
   // not a number - in this case will point at 'x'
}

Используя strtod() для чтения числа с плавающей запятой, вы можете потерять некоторую точность, но это цена, которую вы платите за использование числа с плавающей запятой - в общем, если у вас нет очень веской причины не делать этого, вы всегда должны предпочитать использование числа с плавающей запятой.

Ответ 5 · рейтинг 4 · Thomas

strtof не существует в C89, но есть в C99.

Ответ 6 · рейтинг 4 · Pavel Radzivilovsky

Я предлагаю сначала преобразовать в double, а затем перевести в float. Если относительная разница (f-d)/f больше, чем точность числа с плавающей запятой (примерно 1e-7), то цифр больше, чем можно безопасно хранить в числе с плавающей запятой.

Ответ 7 · рейтинг 2 · Hans Passant

В MSVC вы можете использовать _atoflt(), определенный в stdlib.h

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы