Как я могу проверить, можно ли преобразовать строку в число с плавающей точкой? - PullRequest
2 голосов
/ 20 декабря 2009

Во-первых, мой контекст связан с автором компилятора, которому нужно преобразовать литералы с плавающей запятой (строки) в значения с плавающей запятой / double. За последние 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"?

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

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

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

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

Ответы [ 7 ]

9 голосов
/ 20 декабря 2009

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

Например:

float myfloat;

if(sscanf(str, "%f", &myfloat) != 1)
    /* String could not be converted */
else
    /* String was converted, myfloat is usable */
6 голосов
/ 20 декабря 2009

@ Николас Гой: Я не думаю, что 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 для внутреннего использования и в любом случае использовать его для плавания. Язык в стандарте 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.

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

5 голосов
/ 20 декабря 2009

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

Во-первых, вы не можете проверить 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 () для чтения числа с плавающей точкой, вы можете потерять некоторую точность, но это цена, которую вы платите за использование числа с плавающей точкой - в общем, если у вас нет веских оснований не делать этого, вы всегда должны предпочитать использовать double плавать.

4 голосов
/ 20 декабря 2009

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

4 голосов
/ 20 декабря 2009

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

2 голосов
/ 22 декабря 2009

Поскольку у вас есть значение в 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 голосов
/ 20 декабря 2009

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

...