面试题之 string 转化成double

一、面试中写的代码

主要思路: 把字符串分为 整数、小数、符号三个部分

  • 整数部分的规律是: 整数部值 = 整数部值 * 乘值 +当前值               // 整数部分的乘值为10不变
  • 小数部分的规律是:小数部分值 = 小数部分 + 当前值 * (小数部分乘值)// 小数部分的值不断缩小10倍
  • 符号部分:判断有无’-‘ 存在
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <stdio.h>
#include <string>
#include <iostream>

using namespace std;

double string_to_double(const std::string &str)
{
	double dInteger = 0; // 整数部分
	double dDecimal = 0; // 小数部分
	int sign = 1;		 // 符号
	
	bool bFindPoint = false;
	double dCount = 1;   // 小数部分的值

	for (int i = 0 ; i < str.length(); ++i)
	{
		if (str.at(i) == '-')
		{
			if (i == 0)
			{
				sign = -1;
			}
			else
			{
				return 0;				// error
			}
		}
		else if (str.at(i) == '+')
		{
			if (i == 0)
			{
				sign = 1;
			}
			else
			{
				return 0;				// error
			}
		}
		else if(str.at(i) == '.')
		{
			if (!bFindPoint)
			{
				bFindPoint = true;
			}
			else
			{
				return 0;				// error
			}
		}
		else if (str.at(i) >= '0' && str.at(i) <= '9')
		{
			if (bFindPoint)
			{
				dCount *= 0.1;
				dDecimal += (dCount * ((int)str.at(i) - '0'));
			}
			else
			{
				dInteger *= 10;
				dInteger += (int)str.at(i) - '0';
			}
		}
		else
		{
			return 0;					// error
		}
	}
	return sign * (dInteger + dDecimal);
}

int main()
{
	std::string str = "-1.23456";

	double dValue = string_to_double(str);

	std::cout << "string:" << str << std::endl;
	std::cout << "double:" << dValue << std::endl;

	system("pause");
    return 0;
}

二、标准库代码

经过网上查阅资料才知道,字符串存放double值有很多写法类似:科学计数法、不同进制、三位间隔符(不同的地区 不同的国家 不一样)等等,譬如”123 000 157 100″  “123,000,157,100”  “1.03E+08” “0x15”

  • 第一步:把收到的字符串格式化为本地C语言表达形式
  • 第二步:把格式化后的字符串转化为double

Qt 源码

double asciiToDouble(const char *num, int numLen, bool &ok, int &processed,
                     TrailingJunkMode trailingJunkMode)
{
	// 字符不能为空
    if (*num == '\0') 
	{
        ok = false;
        processed = 0;
        return 0.0;
    }

    ok = true;

	// 优先处理未定义或不可表示的值--类似    -1开根号
    if (qstrcmp(num, "nan") == 0) 
	{
        processed = 3;
        return qt_snan();
    } 
	else if ((num[0] == '-' || num[0] == '+') && qstrcmp(num + 1, "nan") == 0) 
	{
        processed = 0;
        ok = false;
        return 0.0;
    }

	// 超出浮点数的表示范围
    if (qstrcmp(num, "+inf") == 0)
	{
        processed = 4;
        return qt_inf();
    } 
	else if (qstrcmp(num, "inf") == 0) 
	{
        processed = 3;
        return qt_inf();
    } 
	else if (qstrcmp(num, "-inf") == 0) 
	{
        processed = 4;
        return -qt_inf();
    }

    double d = 0.0;
#if !defined(QT_NO_DOUBLECONVERSION) && !defined(QT_BOOTSTRAPPED)
    int conv_flags = (trailingJunkMode == TrailingJunkAllowed) ?
                double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK :
                double_conversion::StringToDoubleConverter::NO_FLAGS;
    double_conversion::StringToDoubleConverter conv(conv_flags, 0.0, qt_snan(), 0, 0);
    d = conv.StringToDouble(num, numLen, &processed);

    if (!qIsFinite(d)) 
	{
        ok = false;
        if (qIsNaN(d)) 
		{
            // Garbage found. We don't accept it and return 0.
            processed = 0;
            return 0.0;
        } else {
            // Overflow. That's not OK, but we still return infinity.
            return d;
        }
    }
#else
    if (qDoubleSscanf(num, QT_CLOCALE, "%lf%n", &d, &processed) < 1)
        processed = 0;

    if ((trailingJunkMode == TrailingJunkProhibited && processed != numLen) || qIsNaN(d))
	{
        // Implementation defined nan symbol or garbage found. We don't accept it.
        processed = 0;
        ok = false;
        return 0.0;
    }

    if (!qIsFinite(d))
		{
        // Overflow. Check for implementation-defined infinity symbols and reject them.
        // We assume that any infinity symbol has to contain a character that cannot be part of a
        // "normal" number (that is 0-9, ., -, +, e).
        ok = false;
        for (int i = 0; i < processed; ++i) 
		{
            char c = num[i];
            if ((c < '0' || c > '9') && c != '.' && c != '-' && c != '+' && c != 'e') 
			{
                // Garbage found
                processed = 0;
                return 0.0;
            }
        }
        return d;
    }
#endif // !defined(QT_NO_DOUBLECONVERSION) && !defined(QT_BOOTSTRAPPED)

    // Otherwise we would have gotten NaN or sorted it out above.
    Q_ASSERT(trailingJunkMode == TrailingJunkAllowed || processed == numLen);

    // Check if underflow has occurred.
    if (isZero(d))
	{
        for (int i = 0; i < processed; ++i)
			{
            if (num[i] >= '1' && num[i] <= '9')
			{
                // if a digit before any 'e' is not 0, then a non-zero number was intended.
                ok = false;
                return 0.0;
            } 
			else if (num[i] == 'e' || num[i] == 'E') 
			{
                break;
            }
        }
    }
    return d;
}


// 格式化字符串---转化为本地C语言表达形式
bool QLocaleData::numberToCLocale(const QChar *str, int len, QLocale::NumberOptions number_options,
                                  CharBuff *result) const
{
    const QChar *uc = str;
    int l = len;
    int idx = 0;

    // 跳过空格键
    while (idx < l && uc[idx].isSpace())
	{
		 ++idx;
	}
    if (idx == l)   // 字符串为空 或者 全部为空格
	{
		return false;
	}
	// 此时的idx 为第一个不为空格的位置

    // 检查尾部的空格数
    for (; idx < l; --l) 
	{
        if (!uc[l - 1].isSpace())
		{
			 break;
		}
    }
	// 此时的l为从后向前第一个不为空格的位置
	// idx---------l  为起始不为空和结尾不为空的标志位

    int group_cnt = 0; 					// 分组符号的个数
    int decpt_idx = -1; 				// 小数点的位置
    int last_separator_idx = -1;		// 上一个分组符号的位置
    int start_of_digits_idx = -1;		// 数字起始位
    int exponent_idx = -1;				// e的位置

    while (idx < l) 
	{
        const QChar in = uc[idx];

        char out = digitToCLocale(in);  // 把单个字符转化为本地c语言表达形式
        if (out == 0)  // 字符0标识空字符--当前表示为digitToCLocale未识别到的字符
		{
            if (in == m_list)
			{
				 out = ';';
			}
            else if (in == m_percent)
			{
				  out = '%';
			}
            // for handling base-x numbers
            else if (in.unicode() >= 'A' && in.unicode() <= 'Z')   // 不同进制
			{
				  out = in.toLower().toLatin1();
			}
            else if (in.unicode() >= 'a' && in.unicode() <= 'z')
			{
				 out = in.toLatin1();
			}
            else			// 错误字符
			{
				 break;
			}
        } 
		else if (out == '.') 			// 小数点
		{
            // 如果多于一个小数点或e后的点则转化失败
            if (decpt_idx != -1 || exponent_idx != -1)
			{
				 return false;
			}
            decpt_idx = idx;
        } 
		else if (out == 'e' || out == 'E')		// e的位置
		{
            exponent_idx = idx;
        }

        if (number_options & QLocale::RejectLeadingZeroInExponent) 
		{
			// 如果存在指数位、且当前的字符不被digitToCLocale识别、且当前不是最后一个字符
            if (exponent_idx != -1 && out == '0' && idx < l - 1) 
			{
                // 指数后面只能为 '+' '-'  '0'~'9' 否则就是错误
                if (result->last() < '0' || result->last() > '9')
				{
					 return false;
				}
            }
        }

        if (number_options & QLocale::RejectTrailingZeroesAfterDot)
		{
            // If we've seen a decimal point and the last character after the exponent is 0, then
            // that is a trailing zero.
            if (decpt_idx >= 0 && idx == exponent_idx && result->last() == '0')
			{
				return false;
			}
        }

        if (!(number_options & QLocale::RejectGroupSeparator)) 
		{
            if (start_of_digits_idx == -1 && out >= '0' && out <= '9')   // 数字起始位
			{
                start_of_digits_idx = idx;
            } 
			else if (out == ',') 
			{
                // 小数点后不允许组字符存在----类似于 123 234 564  或者 123,234,564 或者123;234;564  等' ' ',' ';' 被称为组字符
                if (decpt_idx != -1 || exponent_idx != -1)
				{
					return false;
				}

                // 检查上一个分组符号、和当前的差值--存在部分语言不允许分组https://en.wikipedia.org/wiki/Thousands_separator
                if (last_separator_idx != -1 && idx - last_separator_idx != 4)
				{
					 return false;
				}
				
				// 如果当前是第一个分租符 且前面没出现数字、或者数字的个数不为3 则分组失败
                if (last_separator_idx == -1 && (start_of_digits_idx == -1 || idx - start_of_digits_idx > 3))
				{
					return false;
				}

                last_separator_idx = idx;
                ++group_cnt;    

                // don't add the group separator
                ++idx;
                continue;
            }
			else if (out == '.' || out == 'e' || out == 'E') 
			{
                // 检查上一个分组符号、和当前的差值--存在部分语言不允许分组https://en.wikipedia.org/wiki/Thousands_separator
                if (last_separator_idx != -1 && idx - last_separator_idx != 4)
				{
					return false;
				}

                // 不再处理分租符
                last_separator_idx = -1;		
            }
        }

        result->append(out);

        ++idx;
    } // end while

	// 处理完毕后(或者是break了),判断下  当前位置和分组符位置是否符合要求
    if (!(number_options & QLocale::RejectGroupSeparator))
	{
        // group separator post-processing
        // did we end in a separator?
        if (last_separator_idx + 1 == idx)
		{
			 return false;
		}
        // 数字个数不够-- 类似于:123,254,2  
        if (last_separator_idx != -1 && idx - last_separator_idx != 4)
		{
			return false;
		}
    }

    if (number_options & QLocale::RejectTrailingZeroesAfterDot) 
	{
        // In decimal form, the last character can be a trailing zero if we've seen a decpt.
        if (decpt_idx != -1 && exponent_idx == -1 && result->last() == '0')
		{
			 return false;
		}
    }

	// 结尾符
    result->append('\0');
    return idx == l;
}
double QLocaleData::stringToDouble(const QChar *begin, int len, bool *ok,
                                   QLocale::NumberOptions number_options) const
{
    CharBuff buff;
    // 格式化
    if (!numberToCLocale(begin, len, number_options, &buff)) {
        if (ok != 0)
            *ok = false;
        return 0.0;
    }
    int processed = 0;
    bool nonNullOk = false;
    // 计算值
    double d = asciiToDouble(buff.constData(), buff.length() - 1, nonNullOk, processed);
    if (ok)
        *ok = nonNullOk;
    return d;
}

// 把单个字符转化为本地c语言表达形式
inline char QLocaleData::digitToCLocale(QChar in) const
{
    const ushort tenUnicode = m_zero + 10;

    if (in.unicode() >= m_zero && in.unicode() < tenUnicode)
        return '0' + in.unicode() - m_zero;

    if (in.unicode() >= '0' && in.unicode() <= '9')   	//  数据字符
        return in.toLatin1();

    if (in == m_plus || in == QLatin1Char('+'))
        return '+';

    if (in == m_minus || in == QLatin1Char('-') || in == QChar(0x2212)) // 	0x2212 Unicode 字符'−'
        return '-';

    if (in == m_decimal)			// 小数点
        return '.';

    if (in == m_group)				// 间隔符
        return ',';

    if (in == m_exponential || in == QChar::toUpper(m_exponential))	// e		
        return 'e';

    // In several languages group() is the char 0xA0, which looks like a space.
    // People use a regular space instead of it and complain it doesn't work.
    if (m_group == 0xA0 && in.unicode() == ' ') // 0xA0表示汉字的开始
        return ',';

    return 0;
}

    原文作者:会头痛的可达鸭
    原文地址: https://blog.csdn.net/qq_15821883/article/details/113779118
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞