c# - 为什么 "dtoa.c"包含这么多代码?

标签 c# javascript c floating-point ecma262

我将首先承认我对低级编程的整体知识有点稀疏。我理解许多核心概念,但我并不经常使用它们。 话虽如此,我对 dtoa.c 需要多少代码感到非常震惊。 .

在过去的几个月里,我一直致力于用 C# 实现 ECMAScript,并且我一直在慢慢填补我引擎中的漏洞。昨晚我开始研究 Number.prototype.toString,这在 ECMAScript specification15.7.4.2 部分中有描述。 (pdf) .在 9.8.1 部分,注 3 提供了一个指向 dtoa.c 的链接,但我正在寻找一个挑战,所以我等待查看它。以下是我想出的。

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.c 的代码大约是原来的 40 倍吗?我简直无法想象 C# 的工作效率会高得多。

最佳答案

dtoa.c 包含两个主要函数:dtoa(),将 double 型转换为字符串;strtod(),将字符串转换为 double 型。它还包含很多支持函数,其中大部分是为了自己实现任意精度的算术运算。 dtoa.c 的成名之处在于正确地进行了这些转换,而这通常只能通过任意精度的算术来完成。它还具有在四种不同舍入模式下正确舍入转换的代码。

您的代码仅尝试实现 dtoa() 的等价物,并且由于它使用 float 进行转换,因此并不总是正确的。 (更新:详见我的文章 http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/。)

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

关于c# - 为什么 "dtoa.c"包含这么多代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3173056/

相关文章:

c - 在 Ubuntu 10.04 上编译时未声明 PATH_MAX

c++ - 如何查找变量是分配在堆栈还是堆中?

C - 第一次 recv() 调用后的 "Transport endpoint is not connected"

javascript - 使用 Body.json() Promise 处理非 JSON 响应

javascript - Firestore 子集合中文档的通配符查询

javascript - 编写递归 JavaScript 函数返回 "undefined"

c# - 在 C# 中使用多个接口(interface)键入多态值

c# - 默认路由更改 ASP .Net 5 MVC 6

c# - 自动属性初始化器未填充

c# - 如何在单元测试中处理后台线程中的异常?