Предлагаю вашему вниманию функцию преобразования строки в вещественное число, которой пользуюсь уже достаточно долго.
Достоинства:
полностью самодостаточная компактная функция, не требует даже CRT, так как использует собственный алгоритм
однинаково любезно воспринимает как точку, так и запятую в записи числа; но только один раз
учитывает экспоненциальную форму записи (с литерой 'E' или 'e')
не чувствительна к пробелам в записи числа (актуально для пользовательского интерфейса: пользователь может осознанно или случайно вставить пробел в поле ввода)
не генерирует исключений, если запись не корректна; используется более деликатная схема
Недостатки:
Не поможет извлечь число из потока. Извлекать последовательно идущие данные и выделять числа для разбора Вам придется самостоятельно. (То есть смысла подвать на вход функции "1.234; -7.65;", очевидно, нет.)
Не дружит с разделителями разрядов кроме пробела. Записи вроде "9'999'999" и "1.023,99$" придется предварительно привести к виду "9999999" и "1023,99".
На входе строка для разбора (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;
}
// и так далее...
Буду рад комментариям.