Форум программистов, компьютерный форум CyberForum.ru

Преобразование строки в double - C++

Восстановить пароль Регистрация
 
Рейтинг: Рейтинг темы: голосов - 14, средняя оценка - 4.79
Gepar
 Аватар для Gepar
1175 / 531 / 20
Регистрация: 01.07.2009
Сообщений: 3,512
31.07.2011, 16:21     Преобразование строки в double #1
Собственно есть код:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <iomanip>
using namespace std;
 
 
double atof(const char *nPtr)
{
    int factor=1;//множитель (при поиске целого)/делитель(при поиске числа после точки)
    bool point=0;//флаг. True если точка найдена в числе
    int end=0; //последняя цифра в целой части числа (до точки)
    double result=0;
 
    //поиск точки в строке
    for (int i=0;nPtr[i]!='\0';i++)
    {
        end=i;
        if (nPtr[i]=='.')
        {
            point=i;
            end=i-1;
            break;
        }
    }
 
    //перевод числа до точки в double
    for (int i=end;i>=0;i--,factor*=10)
    {
        //если это число
        if (nPtr[i]<=57 && nPtr[i]>=48)
        {
            result+=(nPtr[i]-48)*factor;
        }
        else
         return 0;
    }
 
    //вывести результат преобразования числа до точки
    cout<<"temp result: "<<result<<endl;
 
    //поиск числа после точки
    factor=10;
    if (point && nPtr[point+1]!='\0')
     for (int i=point+1;nPtr[i]!='\0';i++,factor*=10)
     {
        //если это число
        if (nPtr[i]<=57 && nPtr[i]>=48)
        {
            result+=static_cast<double>(nPtr[i]-48)/static_cast<double>(factor);
        }
 
        //если встречается символ не являющийся цифрой - вернуть текущий результат
        else
         return result;
     }
    return result;
}
 
 
int main()
{
    char a[]="123.";
    cout<<atof(a);
}
Проблема возникла с вычислением числа после точки, точнее вычисляется оно не правильно и мне не понятно почему. Вот например в данном примере где у меня число 123, точка здесь есть, но после неё ничего нет значит условие
C++
1
 if (point && nPtr[point+1]!='\0')
не должно сработать так как сл. символ после точки (end указывает на точку) у меня какраз символ конца строки же, хотя на деле это условие выполняется (печать результата внутри функции до выполнения этого условию показывает это) но почему?
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Евгений М.
1033 / 974 / 53
Регистрация: 28.02.2010
Сообщений: 2,819
Завершенные тесты: 2
31.07.2011, 16:28     Преобразование строки в double #2
Очередной велосипед?
Чем atof из stdlib.h не устроил? Или sscanf (str,"%lf",&result);
?
alex_x_x
бжни
 Аватар для alex_x_x
2441 / 1646 / 84
Регистрация: 14.05.2009
Сообщений: 7,163
31.07.2011, 16:33     Преобразование строки в double #3
C
1
2
3
double value;
sscanf( "234.34", "%lf",  &value );
printf( "%lf\n", value );
Gepar
 Аватар для Gepar
1175 / 531 / 20
Регистрация: 01.07.2009
Сообщений: 3,512
31.07.2011, 16:39  [ТС]     Преобразование строки в double #4
Да блин
Евгений М., я же его не просто так назвал atof, это же не случайность, я надеялся Вы догадаетесь. Собственно одно из заданий Дейтела, вот и делаю "свою реализацию".
castaway
Эксперт С++
4867 / 3006 / 370
Регистрация: 10.11.2010
Сообщений: 11,056
Записей в блоге: 10
Завершенные тесты: 1
31.07.2011, 16:43     Преобразование строки в double #5
У тебя переменная point имеет тип bool, а ты присваиваешь ей значение переменной (i) типа int.
Думаю тебе надо определить point как int.
ValeryLaptev
Эксперт С++
1012 / 791 / 46
Регистрация: 30.04.2011
Сообщений: 1,600
31.07.2011, 16:46     Преобразование строки в double #6
Gepar, чтобы такие задачи щелкать, изучи конечные автоматы.
Вот тебе реализация:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
Листинг 5.5. Реализация конечного преобразователя для перевода дробных чисел
// автомат-преобразователь для дробных констант
// функция вычисления цифры
const int notDigit = -1;            // не цифра
int digit(char ch)
{ string digits = "0123456789";
  int d;
  for(d = 0; d < 10; ++d)
    if(ch == digits[d]) return d;
  return notDigit;              // символ - не цифра
}
// функция вычисления класса символа
enum Symbol { D, P, E, S, O, Class };
Symbol type(char ch)
{ if(isdigit(ch)) return D;
  if(ch =='.')    return P;
  if((ch =='e')||(ch=='E')) return E; 
  if((ch =='-')||(ch=='+')) return S;
  return O;
}
// состояния
enum State { start, s1, s2, s3, s4, s5, s6, s7, finish, error }; 
// обработка состояния ошибки
State Error(const char &ch, Number &D) 
{ Error.set();      // установка флага ошибки 
  return error; 
}
// завершение обработки
State Other(const char &ch, Number &D)
{ return finish; }
// в состоянии start – обработка знака
State startS(const char &ch, Number &D)
{ if (ch == '-') D.signN = -1; 
  return s1;    // первая цифра
}
// в состоянии start и s1 – обработка первой цифры
State startD(const char &ch, Number &D) 
{ D.N = digit(ch);
  return s2;    // целая часть
}
// в состоянии s2 – обработка цифр целой части
State intD(const char &ch, Number &D)
{ D.N = D.N * 10 + digit(ch);
  return s2;    // целая часть
}
// в состоянии s2 – обработка точки
State Point(const char &ch, Number &D)
{ return s3; }  // после точки
 
// в состоянии s2 – обработка E
State Exp(const char &ch, Number &D)
{ return s5; }  // после E
// в состоянии s3 – обработка первой цифры после точки
State float1D(const char &ch, Number &D)
{ D.FP = 0.1;  D.F = digit(ch) * D.FP;
  return s4;    // дробная часть
}
// в состоянии s4 – обработка цифр дробной части
State floatD(const char &ch, Number &D) 
{ D.FP/=10; D.F = D.F + digit(ch) * D.FP;
  return s4;    // дробная часть
}
// в состоянии s5 и s6 – обработка первой цифры порядка
State exp1D(const char &ch, Number &D) 
{ D.P = digit(ch);
  return s7;    // порядок     
}
// в состоянии s6 – обработка знака порядка
State expS(const char &ch, Number &D) 
{ if(ch == '-') D.signP = false;
  return s7;    // порядок 
}
// в состоянии s7 – обработка цифр порядка
State expD(const char &ch, Number &D) 
{ D.P = D.P * 10 + digit(ch);
  return s7;    // порядок
}
typedef State (*Action)(const char &ch, Number &D); 
double Double(const string &str)
{ Action matrix[Class][states] =
//start   s1      s2     s3       s4      s5     s6     s7
//перед   после   целая  после    дроб.   после  после  порядок
//числом  знака   часть  точки    часть   Е      знака Е
{{startD, startD, intD,  float1D, floatD, exp1D, exp1D, expD },// цифра 
 {Error,  Error,  Point, Error,   Error,  Error, Error, Error},// точка
 {Error,  Error,  Exp,   Error,   Exp,    Error, Error, Error},// порядок
 {startS, Error,  Error, Error,   Error,  expS,  Error, Error},// знак
 {Error,  Error,  Other, Error,   Other,  Error, Error, Other} // другой 
};
  Number D;
  double result;            // возвращаемый результат
  size_t n = 0;         // индекс символа в строке
  State s = start;          // начальное состояние
  while((s != finish)&&(s != error)&&(n < str.size()))
  { Symbol t = type(str[n]);        // получили класс символа
    s = matrix[t][s](str[n], D);    // вызов функции
    ++n;                // след символ
  }
// проверка состояния при выходе  
  if ((s==s2)||(s==s4)||(s==s7)||   // финишные состояния
      (s==finish))
  { result = D.N+D.F;
    if(!D.signP) D.P = -D.P;
    result = D.signN * result * pow(10.0, D.P);
  }
  else result = 0;
  return result;
}
Для получения цифры преобразователь использует функцию digit(), аналогичную описанной в листинге 5.3.
Обратите внимание на то, что функции Point() и Exp(), обрабатывающие символы точки и экспоненты, фактически не делают ничего, кроме перехода в новое состояние. Это довольно частое явление при разработке конечных преобразователей. Функция отражает некоторое состояние. «Пустые» состояния необходимы для отслеживания правильной последовательности символов во входной строке.

Функция digit()
C++
1
2
3
4
5
6
7
const int noDigit = -1;
int digit(char ch, byte B = 10)
{ string digits = "0123456789";
  int d;                // возвращаемая цифра 
  for(d = 0; d < B; ++d)
    if(ch == digits[d]) return d;
  return noDiget;
Gepar
 Аватар для Gepar
1175 / 531 / 20
Регистрация: 01.07.2009
Сообщений: 3,512
31.07.2011, 16:55  [ТС]     Преобразование строки в double #7
lazybiz, точно, я сначала хотел ставить только 1 если точка найдена, а потом надумал ставить место где эта точка находится. Спасибо, сейчас заменил на int.Теперь работает нормально. Вот бывает так что затупишь и потом не замечаешь.
ValeryLaptev, что-то сложная у Вас на вид реализация, куча состояний и прочего, сейчас попробую разобраться чисто из любопытства. А вообще: можно за ухом и ногой почесать, но зачем же так усложнять?
ValeryLaptev
Эксперт С++
1012 / 791 / 46
Регистрация: 30.04.2011
Сообщений: 1,600
31.07.2011, 16:58     Преобразование строки в double #8
Gepar, это реализация конечного автомата - НЕ минимального. Каждое состояние - функция. Это чтобы не писать длинный переключатель. Идея данного подхода - у Герба Саттера.
alex_x_x
бжни
 Аватар для alex_x_x
2441 / 1646 / 84
Регистрация: 14.05.2009
Сообщений: 7,163
31.07.2011, 17:47     Преобразование строки в double #9
видов записей вещественных чисел много, вот набрасал вариант на основе конечных автоматов

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include <cstdlib>
#include <iostream>
#include <cassert>
 
#define DEBUG
 
void add_digit( unsigned& value, unsigned digit )
{
   value = value * 10 + digit;
}
 
double _atof( const char* str )
{
  enum { 
        START, 
        WAIT_NUM_POINT_OR_END,
        WAIT_NUM_AFTER_POINT,
        WAIT_NOT_NUM,
        FINISH,
        ERROR
       } state;   
   
   enum {
        NUMBER,
        POINT,
        NOT_NUMBER
        } value;     
   
   char c;
   size_t pos = 0;
   unsigned number = 0;
   unsigned up = 0, down = 0, degree = 1;  
         
   state = START; 
  
   while ( state != FINISH && state != ERROR )
   {
      c = str[pos];
      if( c >= '0' && c<='9' )
      {
         number = c - '0';
         value = NUMBER; 
         // std::cout << "NUMBER " << number << std::endl; 
      }   
      else
      {
         value = ( c == '.' ? POINT : NOT_NUMBER );
         // std::cout << ( value == POINT ?
         //             "POINT" : "NOT_POINT" )
         //           << std::endl; 
      }    
      // std::cout << '(' << state << ')' << std::endl;
      switch( state )
      {
         case START:
           if( value == NUMBER )
           {
              state = WAIT_NUM_POINT_OR_END;
              add_digit( up, number ); 
           }
           else if( value == POINT )
           {
              state = WAIT_NUM_AFTER_POINT;
           }       
           else
           {
              state = ERROR;
           }    
           break;
         case WAIT_NUM_POINT_OR_END:
           if( value == NUMBER )
           {
              state = WAIT_NUM_POINT_OR_END;  
              add_digit( up, number ); 
           }
           else if( value == POINT )
           { 
              state = WAIT_NOT_NUM; 
           }          
           else
           {
              state = FINISH;
           }   
           break; 
         case WAIT_NUM_AFTER_POINT:
           if( value == NUMBER )
           {
              state = WAIT_NOT_NUM;
              add_digit( down, number ); 
              degree *= 10; 
           }
           else
           {
              state = ERROR;
           }
           break;   
         case WAIT_NOT_NUM:
           if( value != NUMBER )
           {
              // std::cout << "FINISH" << std::endl; 
              state = FINISH;
           } 
           else
           {
              add_digit( down, number ); 
              degree *= 10; 
           }              
           break;
         default:
           assert( 0 );  
      }   
      
      ++pos; 
   }    
   
   if( state == FINISH )
   {
      // std::cout << up << '.' << down << std::endl;
      return static_cast<double>(up) +
             static_cast<double>(down) / degree;   
   }
#ifdef DEBUG      
   else
   {
      std::cout << "error" << std::endl;
   }  
#endif
   return 0;
}
 
int main()
{
  const char* szTest[] = { 
                          "123.12",
                          "123", 
                          "0.234",
                          ".34",
                          "sdf",
                          ".23d",
                          ".sd",
                          "1."    
                          };
                         
  for( size_t i=0;i<sizeof( szTest )/sizeof( szTest[0] );++i )
  {
     std::cout << _atof( szTest[i] ) << std::endl;
  }    
       
}
Bash
1
2
3
4
5
6
7
8
9
10
123.12
123
0.234
0.34
error
0
0.23
error
0
1
http://liveworkspace.org/code/0c939a...1efcb707d6ec77

Добавлено через 1 минуту
хотя вижу уже есть вариант на таблицах =)
castaway
Эксперт С++
4867 / 3006 / 370
Регистрация: 10.11.2010
Сообщений: 11,056
Записей в блоге: 10
Завершенные тесты: 1
31.07.2011, 18:13     Преобразование строки в double #10
ValeryLaptev, что это:
C++
1
2
3
4
5
6
7
const int noDigit = -1;
int digit(char ch, byte B = 10)
{ string digits = "0123456789";
  int d;                                // возвращаемая цифра 
  for(d = 0; d < B; ++d)
    if(ch == digits[d]) return d;
  return noDiget;
??!

Так не проще?
C++
1
#define    DIGIT( ch )    ((ch) - '0')
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
31.07.2011, 18:15     Преобразование строки в double
Еще ссылки по теме:

C++ Преобразование long double в char
C++ Преобразование строки в long double
C++ Преобразование TCHAR в double и обратно
Преобразование double в bool C++
Преобразование из string в double C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
ValeryLaptev
Эксперт С++
1012 / 791 / 46
Регистрация: 30.04.2011
Сообщений: 1,600
31.07.2011, 18:15     Преобразование строки в double #11
lazybiz, у меня там функция была, которая в любой системе до 16 выдавала цифру - я ее просто обрезал.
Макросы, конечно можно написать, но этот всегда чревато при развитии проекта.
Yandex
Объявления
31.07.2011, 18:15     Преобразование строки в double
Ответ Создать тему
Опции темы

Текущее время: 13:10. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru