首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >` `atof`‘执行

` `atof`‘执行
EN

Code Review用户
提问于 2016-02-14 20:12:29
回答 3查看 4.6K关注 0票数 5

我已经实现了atof函数。这是我的实现

代码语言:javascript
复制
double atof(const char *str)
{
    double a;           /* the a value in a*10^b */
    double decplace;    /* number to divide by if decimal point is seen */
    double b;           /* The b value (exponent) in a*10^b */

    int sign = 1;       /* stores the sign of a */
    int bsign = 1;      /* stores the sign of b */

    while (*str && isspace(*str))
        ++str;

    if (*str == '+')
        ++str;
    if (*str == '-') {
        sign = -1;
        ++str;
    }
    if ((*str == 'n' || *str == 'N') && 
       (str[1] == 'a' || str[1] == 'A') 
       && (str[2] == 'n' || str[2] == 'N'))
            return NAN * sign;
    if ((*str == 'i' || *str == 'I') && (str[1] == 'n' || str[1] == 'N') && 
        (str[2] == 'f' || str[2] == 'F'))
              return INFINITY * sign;

    for (a = 0; *str && isdigit(*str); ++str)
        a = a * 10 + (*str - '0');

    if (*str == '.')
        ++str;
    for (decplace = 1.; *str && isdigit(*str); ++str, decplace *= 10.)
        a = a * 10. + (*str - '0');

    if (*str == 'e' || *str == 'E') {
        /* if the user types a string starting from e, make the base be 1 */
        if (a == 0)
            a = 1;
        ++str;
        if (*str == '-') {
            bsign = -1;
            ++str;
        }
        if (*str == '+')
            ++str;
        for (b = 0; *str && isdigit(*str); ++str)
            b = b * 10 + (*str - '0');

        b *= bsign;
    }
    else
        b = 0;

    return (a * sign / decplace) * pow(10, b);
}
EN

回答 3

Code Review用户

发布于 2016-02-14 20:28:25

Bug

如果我转换"0e+1",我将返回1,因为这些行:

/\* if the user types a string starting from e, make the base be 1 \*/ if (a == 0) a = 1;

该检查不能仅仅是a == 0,因为'e'之前的部分可能是0,在我的示例中。解决这一问题的一种方法是在解析任何数字之前检查字符串是否以'e'开头,如果是这样,则将a设置为1。

避免调用pow,除非有必要

大多数情况下,您不会解析任何e+exponent部件,但目前无论如何都要调用pow(),这使得您的函数比它可能的速度慢:

返回(a *标志/标记)* pow(10,b);

您应该避免调用pow(),除非您需要它。这里有一种方法(不一定是最好的方法):

代码语言:javascript
复制
return (a * sign / decplace) * ((b == 0) ? 1 : pow(10, b));

时序测试

编辑:一个用户质疑pow()优化是否有用。对于避免调用"55.5"的版本,我在输入pow()上运行了100000000次原始函数。结果:

代码语言:javascript
复制
Original version: 2.55 sec
Modified version: 1.10 sec

我还对代码进行了不同的修改,以避免对1进行额外的乘法,如下所示:

代码语言:javascript
复制
if (*str == 'e' || *str == 'E') {
    // (All the same code here)
    b *= bsign;
    return (a * sign / decplace) * pow(10, b);
}
return a * sign / decplace;

这个更快:

代码语言:javascript
复制
Second modified version: 1.00 sec
票数 10
EN

Code Review用户

发布于 2016-02-15 02:30:37

实现精确的atof函数是令人惊讶的困难,因为没有限制的数字,代码可能需要读取,以确保正确的四舍五入。例如,考虑以下两个值:

  • 12012345.50000000000000000000000000000000000000000000000000000
  • 12012345.50000000000000000000000000000000000000000000000000001

第一轮应为12012345.0f;第二轮应为12012346.0f。函数不需要对输入字符串执行广泛的计算;只要观察到某个点上是否有任何非零位数就足够了,但是处理所有这些情况的逻辑是很棘手的。

票数 4
EN

Code Review用户

发布于 2016-02-18 03:17:40

  1. 溢出值在DBL_MAX附近。使用像"0.0001e310"这样的输入返回inf而不是1.00000e+307。返回(a *标志/标记)* pow(10,b);
  2. 相同代码在DBL_MIN附近丢失精度,在DBL_TRUE_MIN附近丢失总精度。
  3. 同一行代码不必要地失去了精度。要改进#1、2和3,而不是decplace是10的幂,只需使用整数值并将其添加到b。这将大大减少但不会消除DBL_MAX, DBL_MIN,DBL_TRUE_MIN附近的问题。精度损失较小,但不完全消除。IOWs,生成的double将更接近最好的。编辑->还有一个简单的改进:不要使用... * pow(10, b),而是使用(* ... * pow(5, b)) * pow(2,b)。这将允许精确的计算接近于double的极值,而不撤消超过/在流下。
  4. 质量评估。未知-但不应该是。编写一个好的atof()的一部分是测试它的正确性。通过将其重命名为my_atof()并根据标准atof()进行测试将是第一步。然后评估这两个函数的紧密程度。这篇文章漏掉了任何统计数据,所以我想没有做过任何统计。对于任何数学函数,都应该尝试这样的分析,甚至是粗糙的分析。注意,这样的测试代码可能比这个atof()要多得多,而且它是工作的。如果确实对改进算法感兴趣,那么首先编写一个好的atofloat() (只使用float数学),然后将其与strtod()进行比较。
  5. 我没有看到关于在NAN上使用sign的明确共识,所以可能需要也可能不需要使用return NAN * sign;。而且,该代码可能不会在-NAN中提供结果。考虑使用copysign()。一个FP角落的案子。
  6. 迂回:isspace()期望在unsigned char (或EOF)中有一个值。对于有符号的char,下面是一个负值问题。// while (*str && isspace (*str )) // fix with cast或其他方法while (*str && isspace((无符号字符) *str))
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

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

复制
相关文章

相似问题

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