функция разбора действительных чисел на чистом C++
От: Дядюшка Че Россия  
Дата: 07.01.06 08:35
Оценка: 17 (4) +1
Предлагаю вашему вниманию функцию преобразования строки в вещественное число, которой пользуюсь уже достаточно долго.

Достоинства:

Недостатки:

На входе строка для разбора (string) и ссылка на принимающую переменную (var). Если разбор прошел успешно, функция возвращает true, а в var помещается число. В противном случае возвращается false, а переменная var сохраняет свое изначальное состояние.
Таким образом можно
// Допустим, a, b и c - переменные, причем a и b должны быть
// введены обязательно, а c - опционально (по умолчанию, скажем, будет -1).
// pszA, pszB, pszC - соответствующие строки.

    c = -1;
    if (!Parse(pszA, a) || !Parser(pszB, b)) { /*error*/ }
    Parse(pszC, c);


Итак, сама функция!.. Прошу извинить за язык комментариев

// (с)2004, Den Chetverikov

bool Parse(const char* string, long double& var) throw()
{
    bool minus(false);                // true if number is negative
    bool eminus(false);                // true if exponent is negative
    bool sign_awaited(true);
    int f = -1;                       // A number of digits after floating point.
                // When f < 0 we are still parsing an integer part of a number.
    int exp = 0;
    int dig = 0;                            // A number of decimal digits
    int edig = 0;                            // A number of decimal digits in exponent (after 'E')
    long double x = 0;
    char c;                                        // A current character 

    while ((c = *string++),(c != 0))
    {
        if (c == ' ' || c == '\t') continue;

        if (sign_awaited && (c == '+' || c == '-')) 
        {
            minus = (c != '+');
            sign_awaited = false;
            continue;
        }
        if (f < 0 && (c == '.' || c == ',')) 
        {
            f = 0;
            sign_awaited = false;
            continue; 
        }
        if (c == 'e' || c == 'E') 
        {
            sign_awaited = true;
            while ((c = *string++),(c != 0))
            {
                if (c == ' ' || c == '\t') continue;
                if (sign_awaited && (c == '+' || c == '-')) 
                {
                    eminus = (c != '+');
                    sign_awaited = false;
                    continue;
                }
                if (c < '0' || c > '9') return false;
                exp = exp*10 + (c - '0');
                ++edig;
            }
            if (edig == 0) return false;
            if (eminus) exp = -exp;
            break;
        }

        if (c < '0' || c > '9') return false;
        sign_awaited = false;
        ++dig;
        if (f >= 0)
        {
            ++f;
            if (dig >= 25) continue; 
                // All digits after 25th are not significant.
        }
        x = x*10 + (c - '0');
    }

    if (dig == 0) return false;
    f = (f < 0) ? -exp : f-exp;
    while (f < 0) 
    {
        x *= 10;
        ++f; 
    }
    while (f > 0)
    {
        x /= 10;
        --f;
    }
    var = minus ? -x : x;
    return true;
}


Суть алгоритма проста. Число "собирается" из десятичных разрядов в переменной x, как обычное целое. Позиция плавающей точки (или запятой) запоминается отдельно.
Если число записано в экспоненциальной форме (например, 0.499e-2), то считывается еще и экспонента — обычное целое число со знаком. После этого, исходя из позиции запятой и экспоненты, элементарно определяется, на сколько десятичных разрядов необходимо сдвинуть запятую влево (умножением на 10) или вправо (делением на 10). Для наглядности приведу примеры:

  string          |       "целая часть"       |     десятичная экспонента
---------------------------------------------------------------------------
1053.625          |          1053625          |              3
-0,0124           |              124          |             -4
100000            |           100000          |              0
1e20              |                1          |             20
-.25e-4           |               25          |        -2 + (-4) = -6



Функция получилась абсолютно платформенно-независимой, так как использует исключительно манипуляции со встроенными типами C++. Тип результирующей переменной long double выбран, как обеспечивающий максимальную точность.
Переходники для иных типов можно оформить так:


bool Parse(const char* number, double& var)
{
    long double t;
    if(!Parse(number, t)) return false;
    var = static_cast<double>(t);
    return true;
}

// Целые числа получаем путем округления
bool Parse(const char* string, long& var)
{
    long double t;
    if(!Parse(number, t)) return false;
    var = static_cast<long>(t + .5);
    return true;
}

// Для беззнаковых целых разумно будет отказаться от значений < 0.
bool Parse(const char* number, unsigned long& var)
{
    long double t;
    if(!Parse(number, t) || t < 0) return false;
    var = static_cast<unsigned long>(t + .5);
    return true;
}
 
// и так далее...


Буду рад комментариям.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.