網上搜了幾個關於atof()實現的博客,實現的都不是很全面,有的未考慮科學計數法,有的未考慮非法輸入,所以打算自己實現atof()。
函數實現要求:
- 將字符串表示的浮點數轉化爲double類型;
- 可以採用科學計數法,如1.34e3 代表 1340.0;
- 忽略前置空格;
- 能夠考慮正負數值;
- 能夠識別非法輸入;(截斷至非法字符處,如atof(” 12.2c32”)輸出爲12.2)。
函數原型:
double my_atof(const char *str);
爲了提高效率,我們只能遍歷一次字符串,爲此我們需要小數點及e或E的位置。
double my_atof(const char *str){
const char *p = str;
int sign = 1;
while (*p == ' ')++p;//忽略前置空格
if (*p == '-')//考慮是否有符號位
{
sign = -1;
++p;
}
else if (*p == '+')
++p;
int hasDot = 0,hasE = 0;
double integerPart = 0.0,decimalPart = 0.0;
//遇到'e'或'.'字符則退出循環,設置hasE和hasDot。
for (; *p; ++p){
if (isdigit(*p)) //若p指向的字符爲數字則計算當前整數部分的值
integerPart = 10 * integerPart + *p - '0';
else if (*p == '.'){
hasDot = 1;
p++;
break;
}
else if (*p == 'e' || *p == 'E'){
hasE = 1;
p++;
break;
}
else //如果遇到非法字符,則截取合法字符得到的數值,返回結果。
return integerPart;
}
//上一部分循環中斷有三種情況,一是遍歷完成,這種情況下一部分的循環會自動跳過;其次便是是遇到'.'或'e',兩種hasE和hasDot只可能一個爲真,若hasDot爲真則計算小數部分,若hasE爲真則計算指數部分。
int decimalDigits = 1;
int exponential = 0;
for (; *p; p++){
if (hasDot && isdigit(*p))
decimalPart += (*p - '0') / pow(10, decimalDigits++);
else if (hasDot && (*p == 'e' || *p == 'E')) {
integerPart += decimalPart;
decimalPart = 0.0;
hasE = 1;
++p;
break;
}
else if (hasE && isdigit(*p))
exponential = 10 * exponential + *p - '0';
else break;
}
//上一部分較難理解的就是else if (hasDot && (*p == 'e' || *p == 'E')) 這一特殊情況,對於合法的浮點數,出現'.'字符後,仍然有可能是科學計數法表示,但是出現'e'之後,指數部分不能爲小數(這符合<string.h>對atof()的定義)。這種情況變量IntegerPart和decimalPart都是科學計數法的基數,因此有integerPart += decimalPart(這使得IntergerPart的命名可能欠妥,BasePart可能是一種好的選擇)。
//上一部分循環結束一般情況下就能返回結果了,除非遇到前文所述的特殊情況,對於特殊情況需要繼續計算指數。
if (hasE && hasDot)
for (; *p; p++)
if (isdigit(*p))
exponential = 10 * exponential + *p - '0';
return sign * (integerPart * pow(10, exponential) + decimalPart);
}
以上便是我的atof()實現,註釋附有解釋,若有疑問或批評和建議,敬請指正。