На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> перевод из строки в double ---> strtod
    Всем хай!
    Я правильно понимаю, что такой вариант перевода из строки в double является неубиваемым, т е ВСЕ физически возможные косяки перевода будут ловиться?
    ExpandedWrap disabled
      // 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;
      }


    или как-то по-другому такое кодируют на "чистом" Си?
      Строго говоря, ±inf можно считать double, поэтому HUGE_VAL можно за ошибку не считать, но это зависит от конкретики ситуации. Сложнее с NaN. strtod() исправно вернёт NaN и никакой ошибки не зарегистрирует, но NaN, в отличие от inf, точно нечисло.
        Цитата Qraizer @
        Строго говоря, ±inf можно считать double, поэтому HUGE_VAL можно за ошибку не считать

        т е теоретически можно подать на вход число, значение которого РАВНО, например HUGE_VAL (_HUGE)?? Но разве в этом случае errno будет равно ERANGE?

        Цитата Qraizer @
        Сложнее с NaN. strtod() исправно вернёт NaN и никакой ошибки не зарегистрирует, но NaN, в отличие от inf, точно нечисло.

        а как получить этот НАН? приведи, плиз, пример входной строки, когда strtod вернет этот НАН...

        Добавлено
        кстати, этот HUGE_VAL равен
        ExpandedWrap disabled
          System::Double::PositiveInfinity;


        не знаю, как посмотреть это значение...
          Цитата FasterHarder @
          пример входной строки, когда strtod вернет этот НАН...
          Ну можно прямо: d = strtod("+NAN",0);
            вроде все, еще усовершенствовал проверку перевода, в итоге получилось так (как ПРОЩЕ - не знаю, и так сойдет (с) )) )
            ExpandedWrap disabled
              #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;
              }


            циклим юзверя до тех пор, пока не введет разумное значение (т е в капкан сажаем его, а как иначе) + есть образец того, что можно ввести, поэтому выбраться из цикла у него возможность есть...
            и вроде сейчас функция стала норм, правда при вводе чего-нибудь на кириллице там что-то не то получается, но это мне уже НЕ победить, и так сойдет (с) :yes:

            на одном из форуме мне намекали, что достаточно проверять лишь errno на ERANGE, ну, разумеется (как всегда), меня пытались обмануть и этого недостаточно ступодова)

            --------------------
            и примерно по аналогии можно юзать семейства этих функций strto.. переводя всякие инты, лонги и пр.пр.
            Си всегда был очень коварным языком и бесконечно требователен к нюансам, поэтому все равно, думаю, найдутся варианты для функции Is_double_value, когда что-то пойдет не так (с РЯ уже пошло, ну и ладно, все равно СИ НЕ победить)....
            Сообщение отредактировано: FasterHarder -
              Вы этот код для консольного приложения делаете? Для GUI приложения разумнее интерактивную проверку ввода данных в контролы делать.
                Если inf и nan считать корректными значениями, то ок. Что странно, ибо событие ERANGE ты считаешь некорректным.
                У тебя баг: после ERANGE любые значения будут считаться некорректными, т.к. ты забыл сбросить errno в 0.

                Добавлено
                P.S. В C99 проверить на inf и nan легко, там есть функции, в C90 сложнее. С nan несложно, но прикольно: его нужно сравнить с чем-либо, два раза, противоположными операциями. Например, больше нуля и меньше или равно нулю. Обе должны дать ложь. С inf тоже несложно, но неприкольно: inf не изменяется, если его уполовинить. Правда, тем же свойством обладает нуль, который можно отсеять, прибавив к нему 1: inf опять же не изменится.

                Добавлено
                Ну т.е. что-то типа:
                ExpandedWrap disabled
                      /* NaN? */
                      if (!(d <= 0 || d > 0))
                        result = 0;
                      /* inf? */
                      if (d / 2 == d && d + 2 == d)
                        result = 0;
                   
                      errno = 0;
                Сообщение отредактировано: Qraizer -
                  Цитата Qraizer @
                  У тебя баг: после ERANGE любые значения будут считаться некорректными, т.к. ты забыл сбросить errno в 0.

                  кажется я понял, на что ты намекаешь, на это?
                  ExpandedWrap disabled
                        // ловим переполнение или потерю значимости
                        if ( errno == ERANGE )
                        {
                            _set_errno( 0 );
                            result = 0;
                        }


                  кстати еще 1 баг ты не заметил, связанный с конст., тут вообще странная фигня, VS 2010 подчеркивает красным и пишет ошибку, но при этом компилирует и запускает)
                  я про это,так надо:
                  ExpandedWrap disabled
                    int Is_double_value( char* const s )

                  const перед char* дропнул, чтобы VS не материлась...

                  насчет инф и нан ничего не понял, если хочешь и не трудно, то приведи пример входной строки, когда что-то пойдет не так
                    Цитата FasterHarder @
                    кстати еще 1 баг ты не заметил, связанный с конст., тут вообще странная фигня, VS 2010 подчеркивает красным и пишет ошибку, но при этом компилирует и запускает)
                    Да, упустил. Согласно Стандарту языка, если квалификатор указательного типа появляется где-то в середине его синопсиса, то он должен быть продолжен вправо до упора. Т.е. если у тебя const char* data, то взяв на него поинтер и получив const char**, ты нарушил Стандарт, и нужно, чтобы было const char* const*. (На самом деле необязательно, можно привести контексты, где это будет нормальным.) Но тогда будет аналогичный баг в точке вызова strtod(), если только не поставить явный каст. Что делать, синопсис strtod() неудачный. В Плюсах это бы решили перегрузкой, но в C невозможно.
                    P.S. Причина такого требования Стандарта в скрытой возможности менять константы. Вот, например:
                    ExpandedWrap disabled
                      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(), например, это уже отнюдь не будет искусственным.
                    Сообщение отредактировано: Qraizer -
                      Qraizer, спс за пояснения
                        опять столкнулся с проблемой перевода, только теперь в функции STRTOUL, т е в беззнаковое целое.
                        перерыл кучу ссылок в инете и даже залез в офиц.стандарт С89 - ну, нет нигде полных примеров((

                        главная проблема, что, если число-строка = "-20", то оно конвертируется по модулю в допустимое и все эти ERANGE не работают!(
                        ---------------------
                        В итоге, пока локально победил так. Договорился для себя, что мне хватит беззнакового целого 2байтового ---> unsigned short.
                        Прочитал строку с клавиатуры. И затем, используя функцию STRTOL преобразовал просто в 4рех байтовое знаковое ---> int (long).
                        Затем стандартные проверки на переполнение и пр. и последняя проверка, это на попадание в диапазон unsigned short
                        ExpandedWrap disabled
                                  // если число не натуральное или выходит за пределы 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% всех проблем это закрывает на УРА...
                          Отрицательные значения она принимает, как говорится, by design. Не знаю, зачем так сделано, наверное как логическое следствие того, что беззнаковая арифметика никогда не вызывает переполнений, она всегда по модулю 2sideof(type)*CHAR_BIT.
                            Qraizer, если упростить, то мой вариант решения приемлем получается? Т е для проверки на ushort делаю перевод в int и потом проверяю границы на попадание в ushort???
                              Если тебе нужно ввести значение, меньшее int, то прямых функций всё равно нет. Т.к. твой беззнаковый 16-битный целиком помещается в знаковый 32-битный, то ввести его и искусственно ограничить итог проверками вполне разумное решение.
                                Qraizer, вопрос СО ЗВЕЗДОЧКОЙ)
                                а если мне нужно проверить на попадание в unsigned int (long). В этом случае как выкручиваться??((

                                Добавлено
                                т е я к тому. что правая граница для UINT ведь больше, чем для знакового INT и то, что является переполнением для INT для UINT может быть допустимым. Если брать STRTOUL (C89), то там нет норм. проверки на ERANGE.

                                или нужно вообще перевести в double, а потом кастовать в UINT с проверкой на допустимый диапазон...??
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,1029 ]   [ 16 queries used ]   [ Generated: 2.05.24, 01:04 GMT ]