在对这个问题的回答中,我提到了最大的努力。我试着解释一下我的意思。请记住,实现是有意不完整的(缺少的功能,如NAN
是微不足道的添加),并特别关注数值稳定性。
令人惊讶的是,很难正确地实现它,但它得到了回报:结果与stdlib
实现相匹配,即使是对于我能想到的最琐碎的输入也是如此。
然而,normalize
是我编写过的最混乱的代码。它计算了三个结果--光是这一结果就足以引起一些人的注意。而且memmove
看上去特别不合适。
欢迎所有建议。
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
static char * drop_leading_zeroes(char * start)
{
return start + strspn(start, "0");
}
static char * digit_span(char * start)
{
return start + strspn(start, "0123456789");
}
static int normalize(char ** start, char ** end)
{
int shift = 0;
*start = drop_leading_zeroes(*start);
if (isdigit(**start)) {
*end = digit_span(*start);
shift = *end - *start;
if (**end == '.') {
*end = digit_span(*end + 1);
memmove(*start + 1, *start, shift);
*start += 1;
}
} else if (**start == '.') {
*start += 1;
*end = drop_leading_zeroes(*start);
shift = *start - *end;
*start = *end;
*end = digit_span(*end);
} else {
*end = *start;
}
return shift;
}
static double compute_mantissa(char * start, char * end)
{
double result = 0.0;
while (end != start) {
result = (result + (*--end - '0')) / 10;
}
return result;
}
double my_atof(char * s)
{
bool minus = false;
switch(*s) {
case '-': minus = true;
case '+': ++s; break;
}
char * end;
int exponent = normalize(&s, &end);
double mantissa = compute_mantissa(s, end);
if (minus) {
mantissa = -mantissa;
}
if (tolower(*end) == 'e') {
exponent += atoi(end + 1);
}
return mantissa * pow(10, exponent);
}
发布于 2016-02-19 09:10:40
真正的atof()
不会修改输入字符串,这样做似乎是错误的。它可能会导致各种意想不到的行为。例如,如果传入只读部分中的字符串文字,则程序可能崩溃:
// Segmentation violation!
double val = my_atof("55.5");
或者你这样做会得到一个令人惊讶的结果:
double val1 = my_atof(str);
double val2 = my_atof(str);
// val1 != val2 because the string mutated
您可以在不修改输入的情况下进行一些小的更改来使您的atof()
工作。首先,删除修改字符串的行:
memmove(\*start + 1, \*start, shift); \*start += 1;
这些行的唯一目的是擦除将整个部分从小数部分分离出来的'.'
。然后修改compute_mantissa()
以跳过任何'.'
字符:
static double compute_mantissa(const char * start, const char * end)
{
double result = 0.0;
while (end != start) {
char c = *--end;
if (c == '.')
continue;
result = (result + (c - '0')) / 10;
}
return result;
}
最后一步是将每个char *
更改为const char *
,因为您不打算修改任何字符串。您可以看到,我已经对上面的compute_mantissa()
做过了。
的水平
在normalize()
中,在整个函数中对指向指针的指针进行操作。我发现,如果您使用临时变量,您可以减少间接级别,代码将更容易阅读。例如:
static int normalize(const char ** pStart, const char ** pEnd)
{
int shift = 0;
const char * start = *pStart;
const char * end;
start = drop_leading_zeroes(start);
if (isdigit(*start)) {
end = digit_span(start);
shift = end - start;
if (*end == '.') {
end = digit_span(end + 1);
}
} else if (*start == '.') {
start += 1;
end = drop_leading_zeroes(start);
shift = start - end;
start = end;
end = digit_span(end);
} else {
end = start;
}
*pStart = start;
*pEnd = end;
return shift;
}
https://codereview.stackexchange.com/questions/120453
复制相似问题