代码之家  ›  专栏  ›  技术社区  ›  ChaosPandion

为什么“dtoa.c”包含这么多代码?

  •  18
  • ChaosPandion  · 技术社区  · 14 年前

    我将第一个承认我对低级编程的总体知识有点稀缺。我了解许多核心概念,但我不经常使用它们。 据说我对需要多少代码感到非常惊讶 dtoa.c .

    在过去的几个月里,我一直在致力于C中的ECMAScript实现,我也一直在放慢填补我的引擎漏洞的速度。昨晚我开始工作 number.prototype.to字符串 在第节中描述 7.7.4.2 ECMAScript specification (pdf) . 断面 7.8 ,注3提供了一个链接 DtoA 但我在找一个挑战,所以我等着看。以下是我的想法。

    private IDynamic ToString(Engine engine, Args args)
    {
        var thisBinding = engine.Context.ThisBinding;
        if (!(thisBinding is NumberObject) && !(thisBinding is NumberPrimitive))
        {
            throw RuntimeError.TypeError("The current 'this' must be a number or a number object.");
        }
    
        var num = thisBinding.ToNumberPrimitive();
    
        if (double.IsNaN(num))
        {
            return new StringPrimitive("NaN");
        }
        else if (double.IsPositiveInfinity(num))
        {
            return new StringPrimitive("Infinity");
        }
        else if (double.IsNegativeInfinity(num))
        {
            return new StringPrimitive("-Infinity");
        }
    
        var radix = !args[0].IsUndefined ? args[0].ToNumberPrimitive().Value : 10D;
    
        if (radix < 2D || radix > 36D)
        {
            throw RuntimeError.RangeError("The parameter [radix] must be between 2 and 36.");
        }
        else if (radix == 10D)
        {
            return num.ToStringPrimitive();
        }
    
        var sb = new StringBuilder();
        var isNegative = false;
    
        if (num < 0D)
        {
            isNegative = true;
            num = -num;
        }
    
        var integralPart = Math.Truncate(num);
        var decimalPart = (double)((decimal)num.Value - (decimal)integralPart);
        var radixChars = RadixMap.GetArray((int)radix);
    
        if (integralPart == 0D)
        {
            sb.Append('0');
        }
        else
        {
            var integralTemp = integralPart;
            while (integralTemp > 0)
            {
                sb.Append(radixChars[(int)(integralTemp % radix)]);
                integralTemp = Math.Truncate(integralTemp / radix);
            }
        }
    
        var count = sb.Length - 1;
        for (int i = 0; i < count; i++)
        {
            var k = count - i;
            var swap = sb[i];
            sb[i] = sb[k];
            sb[k] = swap;
        }
    
        if (isNegative)
        {
            sb.Insert(0, '-');
        }
    
        if (decimalPart == 0D)
        {
            return new StringPrimitive(sb.ToString());
        }
    
        var runningValue = 0D;
        var decimalIndex = 1D;
        var decimalTemp = decimalPart;
    
        sb.Append('.');
        while (decimalIndex < 100 && decimalPart - runningValue > 1.0e-50)
        {
            var result = decimalTemp * radix;
            var integralResult = Math.Truncate(result);
            runningValue += integralResult / Math.Pow(radix, decimalIndex++);
            decimalTemp = result - integralResult;
            sb.Append(radixChars[(int)integralResult]);
        }
    
        return new StringPrimitive(sb.ToString());
    }
    

    有更多低层编程经验的人能解释为什么 DtoA 大约有40倍的代码?我只是无法想象C的效率会更高。

    5 回复  |  直到 11 年前
        1
  •  38
  •   Rick Regan    14 年前

    c包含两个主要函数:dtoa()将double转换为string,strtod()将string转换为double。它还包含了大量的支持函数,其中大部分是为自己实现任意精度的算术。dtoa.c声称自己的名气是让这些转换正确,而这通常只能用任意精度的算术来完成。在四种不同的取整模式下,它还具有正确的代码取整转换。

    您的代码只尝试实现等效的dtoa(),因为它使用浮点进行转换,所以并不总是正确的。(更新:参见我的文章 http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/ 详情。

    (我在博客上写了很多关于这个的文章, http://www.exploringbinary.com/ . 我最近七篇文章中有六篇是关于strtod()转换的。通读它们,看看正确地进行四舍五入转换有多复杂。)

        2
  •  7
  •   Matthew Slattery    12 年前

    生产 好的 十进制和二进制浮点表示之间的转换结果是一个相当困难的问题。

    困难的主要来源是许多小数,即使是简单的小数,也不能 准确地 用二进制浮点表示——例如, 0.5 可以(显然),但是 0.1 不能。另一方面(从二进制到十进制),你通常不希望得到绝对准确的结果(例如,最接近的数字的精确十进制值 零点一 可在符合IEEE-754标准中表示。 double 实际上是 0.1000000000000000055511151231257827021181583404541015625 )所以你通常需要一些取整。

    所以,转换通常涉及近似。良好的转换程序保证生成 最近的 特定(字大小或位数)约束内的可能近似值。这就是大部分复杂性的来源。

    看看评论中引用的文章 dtoa.c 实施,克林格 How to Read Floating Point Numbers Accurately 为了解决这个问题,也许还有大卫·M·盖伊(作者)的论文, Correctly Rounded Binary-Decimal and Decimal-Binary Conversions .

    (此外,更一般地说: What Every Computer Scientist Should Know About Floating Point Arithmetic )

        3
  •  4
  •   dkackman Srinivas Kokkula    14 年前

    基于对它的快速浏览,相当数量的C版本正在处理多个平台,例如它看起来像是在编译器(C&AM+C++)、BITNESS、浮点实现和平台上一般可用的; #define 可配置性。

        4
  •  4
  •   Lajnold    14 年前

    我还认为dtoa.c中的代码可能更有效(独立于语言)。例如,它似乎在做一些小动作,在专家的手中,这通常意味着速度。因为速度的原因,我假设它只是使用了一个不太直观的算法。

        5
  •  2
  •   Pavel Radzivilovsky    14 年前

    简短回答:因为 dtoa.c 作品。

    这正是调试良好的产品和NIH原型之间的区别。