低レベルのプログラミングに関する私の全体的な知識が少しまばらであることを最初に認めます。コアとなる概念の多くは理解していますが、定期的に使用することはありません。そうは言っても、 dtoa.cに必要なコードの量には本当に驚きました。
過去 2 か月間、私は C# での ECMAScript の実装に取り組んできましたが、エンジンの穴を埋めるのに時間がかかりました。昨夜、ECMAScript 仕様(pdf)のセクション15.7.4.2で説明されているNumber.prototype.toStringの作業を開始しました。セクション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# がそれほど生産的であるとは想像できません。