首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >‘`atof`’重温

‘`atof`’重温
EN

Code Review用户
提问于 2016-02-18 20:29:04
回答 1查看 217关注 0票数 4

在对这个问题的回答中,我提到了最大的努力。我试着解释一下我的意思。请记住,实现是有意不完整的(缺少的功能,如NAN是微不足道的添加),并特别关注数值稳定性。

令人惊讶的是,很难正确地实现它,但它得到了回报:结果与stdlib实现相匹配,即使是对于我能想到的最琐碎的输入也是如此。

然而,normalize是我编写过的最混乱的代码。它计算了三个结果--光是这一结果就足以引起一些人的注意。而且memmove看上去特别不合适。

欢迎所有建议。

代码语言:javascript
运行
复制
#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);
}
EN

回答 1

Code Review用户

发布于 2016-02-19 09:10:40

修改输入字符串

真正的atof()不会修改输入字符串,这样做似乎是错误的。它可能会导致各种意想不到的行为。例如,如果传入只读部分中的字符串文字,则程序可能崩溃:

代码语言:javascript
运行
复制
// Segmentation violation!
double val = my_atof("55.5");

或者你这样做会得到一个令人惊讶的结果:

代码语言:javascript
运行
复制
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()以跳过任何'.'字符:

代码语言:javascript
运行
复制
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()中,在整个函数中对指向指针的指针进行操作。我发现,如果您使用临时变量,您可以减少间接级别,代码将更容易阅读。例如:

代码语言:javascript
运行
复制
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;
}
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/120453

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档