Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.119.131.72] |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Всем хай!
Я правильно понимаю, что такой вариант перевода из строки в double является неубиваемым, т е ВСЕ физически возможные косяки перевода будут ловиться? // 0 - not double; 1 - yes double // s - checked string-double value int Is_double_value( char* s ) { int result = 1; char* end = s; double d = strtod( s, &end ); // ловим переполнение или потерю значимости if ( ( errno == ERANGE ) && ( ( d == HUGE_VAL ) || ( d == -HUGE_VAL ) ) ) result = 0; // если пустая строка (или состоит только из разделителей) if ( ( end - s ) == 0 ) result = 0; return result; } или как-то по-другому такое кодируют на "чистом" Си? |
Сообщ.
#2
,
|
|
|
Строго говоря, ±inf можно считать double, поэтому HUGE_VAL можно за ошибку не считать, но это зависит от конкретики ситуации. Сложнее с NaN. strtod() исправно вернёт NaN и никакой ошибки не зарегистрирует, но NaN, в отличие от inf, точно нечисло.
|
Сообщ.
#3
,
|
|
|
Цитата Qraizer @ Строго говоря, ±inf можно считать double, поэтому HUGE_VAL можно за ошибку не считать т е теоретически можно подать на вход число, значение которого РАВНО, например HUGE_VAL (_HUGE)?? Но разве в этом случае errno будет равно ERANGE? Цитата Qraizer @ Сложнее с NaN. strtod() исправно вернёт NaN и никакой ошибки не зарегистрирует, но NaN, в отличие от inf, точно нечисло. а как получить этот НАН? приведи, плиз, пример входной строки, когда strtod вернет этот НАН... Добавлено кстати, этот HUGE_VAL равен System::Double::PositiveInfinity; не знаю, как посмотреть это значение... |
Сообщ.
#4
,
|
|
|
Цитата FasterHarder @ Ну можно прямо: d = strtod("+NAN",0); пример входной строки, когда strtod вернет этот НАН... |
Сообщ.
#5
,
|
|
|
вроде все, еще усовершенствовал проверку перевода, в итоге получилось так (как ПРОЩЕ - не знаю, и так сойдет (с) )) )
#include <stdio.h> #include <stdlib.h> #include <locale.h> #include <string.h> #include <math.h> // 0 - not double; 1 - yes doulbe // s - checked string-double value int Is_double_value( const char* const s ) { #define STRING_TERMINATOR '\0' int result = 1; char* end = s; // попытка перевода из строки-числа в число-число double d = strtod( s, &end ); // мне НЕ важно, переполнение случилось или еще какая-то шняга, мне важен отлов факта ошибки, поэтому HUGE_VAL не нужны //if ( ( errno == ERANGE ) && ( ( d == HUGE_VAL ) || ( d == -HUGE_VAL ) ) ) // ловим переполнение или потерю значимости if ( errno == ERANGE ) result = 0; // если за числом во входной строке остались символы, не относящиеся к числу if ( *end != STRING_TERMINATOR ) result = 0; // если пустая строка (или состоит только из разделителей) if ( ( end - s ) == 0 ) result = 0; return result; } int main( void ) { #define LEN 250 char s[ LEN ]; char* end = s; double x; setlocale( LC_ALL, "Russian" ); do { printf( "Введите дробное число (пример: 17,04): " ); fflush( stdin ); gets( s ); } while ( ! Is_double_value( s ) ); // гарантированный перевод из строки-числа в число-число x = strtod( s, &end ); // выводим число на экран (для сверки перевода) printf( "Х = %lf", x ); printf( "\n\n" ); system( "pause" ); return EXIT_SUCCESS; } циклим юзверя до тех пор, пока не введет разумное значение (т е в капкан сажаем его, а как иначе) + есть образец того, что можно ввести, поэтому выбраться из цикла у него возможность есть... и вроде сейчас функция стала норм, правда при вводе чего-нибудь на кириллице там что-то не то получается, но это мне уже НЕ победить, и так сойдет (с) на одном из форуме мне намекали, что достаточно проверять лишь errno на ERANGE, ну, разумеется (как всегда), меня пытались обмануть и этого недостаточно ступодова) -------------------- и примерно по аналогии можно юзать семейства этих функций strto.. переводя всякие инты, лонги и пр.пр. Си всегда был очень коварным языком и бесконечно требователен к нюансам, поэтому все равно, думаю, найдутся варианты для функции Is_double_value, когда что-то пойдет не так (с РЯ уже пошло, ну и ладно, все равно СИ НЕ победить).... |
Сообщ.
#6
,
|
|
|
Вы этот код для консольного приложения делаете? Для GUI приложения разумнее интерактивную проверку ввода данных в контролы делать.
|
Сообщ.
#7
,
|
|
|
Если inf и nan считать корректными значениями, то ок. Что странно, ибо событие ERANGE ты считаешь некорректным.
У тебя баг: после ERANGE любые значения будут считаться некорректными, т.к. ты забыл сбросить errno в 0. Добавлено P.S. В C99 проверить на inf и nan легко, там есть функции, в C90 сложнее. С nan несложно, но прикольно: его нужно сравнить с чем-либо, два раза, противоположными операциями. Например, больше нуля и меньше или равно нулю. Обе должны дать ложь. С inf тоже несложно, но неприкольно: inf не изменяется, если его уполовинить. Правда, тем же свойством обладает нуль, который можно отсеять, прибавив к нему 1: inf опять же не изменится. Добавлено Ну т.е. что-то типа: /* NaN? */ if (!(d <= 0 || d > 0)) result = 0; /* inf? */ if (d / 2 == d && d + 2 == d) result = 0; errno = 0; |
Сообщ.
#8
,
|
|
|
Цитата Qraizer @ У тебя баг: после ERANGE любые значения будут считаться некорректными, т.к. ты забыл сбросить errno в 0. кажется я понял, на что ты намекаешь, на это? // ловим переполнение или потерю значимости if ( errno == ERANGE ) { _set_errno( 0 ); result = 0; } кстати еще 1 баг ты не заметил, связанный с конст., тут вообще странная фигня, VS 2010 подчеркивает красным и пишет ошибку, но при этом компилирует и запускает) я про это,так надо: int Is_double_value( char* const s ) const перед char* дропнул, чтобы VS не материлась... насчет инф и нан ничего не понял, если хочешь и не трудно, то приведи пример входной строки, когда что-то пойдет не так |
Сообщ.
#9
,
|
|
|
Цитата FasterHarder @ Да, упустил. Согласно Стандарту языка, если квалификатор указательного типа появляется где-то в середине его синопсиса, то он должен быть продолжен вправо до упора. Т.е. если у тебя const char* data, то взяв на него поинтер и получив const char**, ты нарушил Стандарт, и нужно, чтобы было const char* const*. (На самом деле необязательно, можно привести контексты, где это будет нормальным.) Но тогда будет аналогичный баг в точке вызова strtod(), если только не поставить явный каст. Что делать, синопсис strtod() неудачный. В Плюсах это бы решили перегрузкой, но в C невозможно.кстати еще 1 баг ты не заметил, связанный с конст., тут вообще странная фигня, VS 2010 подчеркивает красным и пишет ошибку, но при этом компилирует и запускает) P.S. Причина такого требования Стандарта в скрытой возможности менять константы. Вот, например: const char *data= "It's a string"; char *tmp; const char**ptr = &tmp; /* "законное" (нет) добавление const */ *ptr = data; /* законное tmp = data */ tmp[0] = 'i'; /* теперь можно менять data через tmp */ Добавлено Цитата FasterHarder @ Та просто вбить inf или nan в ответ на запрос gets(). Понятно, что в твоём случае это искусственный пример, но ведь возможный. У тебя ж Is_double_value() универсальная, вроде. Когда через полгода ты её заюзаешь в другом контексте, а там строки будут приходить из sprintf(), например, это уже отнюдь не будет искусственным. насчет инф и нан ничего не понял, если хочешь и не трудно, то приведи пример входной строки, когда что-то пойдет не так |
Сообщ.
#10
,
|
|
|
Qraizer, спс за пояснения
|
Сообщ.
#11
,
|
|
|
опять столкнулся с проблемой перевода, только теперь в функции STRTOUL, т е в беззнаковое целое.
перерыл кучу ссылок в инете и даже залез в офиц.стандарт С89 - ну, нет нигде полных примеров(( главная проблема, что, если число-строка = "-20", то оно конвертируется по модулю в допустимое и все эти ERANGE не работают!( --------------------- В итоге, пока локально победил так. Договорился для себя, что мне хватит беззнакового целого 2байтового ---> unsigned short. Прочитал строку с клавиатуры. И затем, используя функцию STRTOL преобразовал просто в 4рех байтовое знаковое ---> int (long). Затем стандартные проверки на переполнение и пр. и последняя проверка, это на попадание в диапазон unsigned short // если число не натуральное или выходит за пределы unsigned short if ( ( n <= 0 ) || ( n > USHRT_MAX ) ) continue; и вроде это вполне норм. работает. Но я НЕ понимаю, как воспользоваться напрямую функцией STRTOUL, т к она игнорирует все эти ERRANGE, когда на вход подается "-17". Объясните, плиз, я правильно сделал или все-таки можно получить полноценное использование STRTOUL?? Добавлено т е на данный момент у меня полная проверка состоит из 4рех этапов: 1. переполнение в любую сторону ----> errno == ERANGE 2. если за числом есть недопустимые символы ---> *end == 0 3. если пустая строка или состоит только НЕ из цифр ---> end - s == 0 4. проверка на попадание в диапазон unsigned short 99% всех проблем это закрывает на УРА... |
Сообщ.
#12
,
|
|
|
Отрицательные значения она принимает, как говорится, by design. Не знаю, зачем так сделано, наверное как логическое следствие того, что беззнаковая арифметика никогда не вызывает переполнений, она всегда по модулю 2sideof(type)*CHAR_BIT.
|
Сообщ.
#13
,
|
|
|
Qraizer, если упростить, то мой вариант решения приемлем получается? Т е для проверки на ushort делаю перевод в int и потом проверяю границы на попадание в ushort???
|
Сообщ.
#14
,
|
|
|
Если тебе нужно ввести значение, меньшее int, то прямых функций всё равно нет. Т.к. твой беззнаковый 16-битный целиком помещается в знаковый 32-битный, то ввести его и искусственно ограничить итог проверками вполне разумное решение.
|
Сообщ.
#15
,
|
|
|
Qraizer, вопрос СО ЗВЕЗДОЧКОЙ)
а если мне нужно проверить на попадание в unsigned int (long). В этом случае как выкручиваться??(( Добавлено т е я к тому. что правая граница для UINT ведь больше, чем для знакового INT и то, что является переполнением для INT для UINT может быть допустимым. Если брать STRTOUL (C89), то там нет норм. проверки на ERANGE. или нужно вообще перевести в double, а потом кастовать в UINT с проверкой на допустимый диапазон...?? |