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

C# “为什么?”;dtoa.c“;包含这么多代码?,c#,javascript,c,floating-point,ecma262,C#,Javascript,C,Floating Point,Ecma262,我将是第一个承认我对低级编程的总体知识有点缺乏的人。我理解许多核心概念,但我不经常使用它们。 话虽如此,我还是对这个系统需要多少代码感到震惊 在过去的几个月里,我一直在致力于C#中的ECMAScript实现,我一直在缓慢地填补引擎中的漏洞。昨天晚上,我开始研究Number.prototype.toString,这在本手册的15.7.4.2节中进行了描述。在第9.8.1节中,注释3提供了到dtoa.c的链接,但我正在寻找挑战,所以等待查看。下面是我想到的 private IDynamic ToSt

我将是第一个承认我对低级编程的总体知识有点缺乏的人。我理解许多核心概念,但我不经常使用它们。 话虽如此,我还是对这个系统需要多少代码感到震惊

在过去的几个月里,我一直在致力于C#中的ECMAScript实现,我一直在缓慢地填补引擎中的漏洞。昨天晚上,我开始研究Number.prototype.toString,这在本手册的15.7.4.2节中进行了描述。在第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());
}
private IDynamic ToString(引擎引擎,Args Args)
{
var thisBinding=engine.Context.thisBinding;
如果(!(此绑定是NumberObject)和(!(此绑定是NumberPrimitive))
{
抛出RuntimeError.TypeError(“当前的'this'必须是数字或数字对象。”);
}
var num=thisBinding.ToNumberPrimitive();
if(double.IsNaN(num))
{
返回新字符串原语(“NaN”);
}
else if(double.IsPositiveInfinity(num))
{
返回新字符串原语(“无穷大”);
}
else if(double.IsNegativeInfinity(num))
{
返回新的StringPrimitive(“-无穷大”);
}
变量基数=!args[0]。是否未定义?args[0]。ToNumberPrimitive()。值:10D;
if(基数<2D | |基数>36D)
{
抛出RuntimeError.RangeError(“参数[radix]必须介于2和36之间”);
}
否则如果(基数==10D)
{
返回num.ToStringPrimitive();
}
var sb=新的StringBuilder();
var为负=假;
如果(数值<0D)
{
isNegative=true;
num=-num;
}
var integralPart=数学截断(num);
var decimalPart=(双精度)((十进制)数值-(十进制)整数部分);
var radixChars=RadixMap.GetArray((int)基数);
如果(积分部分==0D)
{
某人附加('0');
}
其他的
{
var integralTemp=集成部分;
而(积分温度>0)
{
sb.追加(radixChars[(int)(integralTemp%基数)];
integralTemp=数学截断(integralTemp/基数);
}
}
变量计数=sb长度-1;
for(int i=0;i1.0e-50)
{
var结果=小数点*基数;
var integralResult=数学截断(结果);
runningValue+=integralResult/Math.Pow(基数,小数位数++);
decimalTemp=结果-积分结果;
sb.追加(radixChars[(int)integralResult]);
}
返回新的StringPrimitive(sb.ToString());
}

在低级编程方面有更多经验的人能解释一下为什么dtoa.c的代码大约是它的40倍吗?我无法想象C会有这么高的效率。

快速浏览一下,相当多的C版本都在处理多个平台,例如,该文件似乎要在编译器(C&C++)、比特数、浮点实现和平台之间通用;有了大量的
#define
可配置性。

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

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

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

因此,转换通常涉及近似。良好的转换例程保证在特定(字长或位数)约束内产生尽可能接近的近似值。这就是大部分复杂性的来源

请看一下
dtoa.c
实现Clinger's顶部评论中引用的论文,了解问题的一点味道;也许还有大卫·M·盖伊(作者)的论文


(另外,更一般地说:)

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

您的代码只尝试实现dtoa()的等效项,并且由于它使用浮点进行转换,因此并不总是正确的。(更新:有关详细信息,请参阅我的文章。)

(我在我的博客上写了很多关于这方面的文章。我最近七篇文章中有六篇是关于strod()转换的。通读一遍,看看正确进行四舍五入转换有多复杂