Cha*_*ion 19 javascript c c# floating-point ecma262
我将是第一个承认我对低级编程的整体知识有点稀疏的人.我理解许多核心概念,但我不会定期使用它们.话虽如此,我对dtoa.c需要多少代码感到非常震惊.
在过去的几个月里,我一直在使用C#进行ECMAScript实现,而且我一直在减慢填充引擎中的漏洞.昨晚我开始研究Number.prototype.toString,它在ECMAScript规范(pdf)的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());
}
Run Code Online (Sandbox Code Playgroud)
有没有更多低级编程经验的人可以解释为什么dtoa.c的代码大约是其40倍?我无法想象C#会更高效.
Ric*_*gan 40
dtoa.c包含两个主要函数:dtoa(),它将double转换为字符串,strtod()将字符串转换为double.它还包含许多支持函数,其中大部分都是为自己实现的任意精度算术.dtoa.c声名鹊起就是让这些转换正确,而这通常只能通过任意精度算术来完成.它还具有在四种不同的舍入模式下正确舍入转换的代码.
您的代码只尝试实现dtoa()的等价物,并且因为它使用浮点来进行转换,所以并不总是正确.(更新:有关详细信息,请参阅我的文章http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/.)
(我在我的博客上写了很多关于这个的文章,http://www.exploringbinary.com/.我的最后七篇文章中有六篇仅关于strtod()转换.仔细阅读它们,看看它有多复杂正确舍入转换.)
为十进制和二进制浮点表示之间的转换产生良好结果是一个相当困难的问题.
困难的主要来源是许多小数部分,即使是简单的小数,也不能使用二进制浮点精确表示 - 例如,0.5可以(显然),但0.1不能.而且,走另一条路(从二进制到十进制),你通常不希望绝对准确的结果(例如,最近数的精确十进制值0.1可以在兼容IEEE-754的代表double实际上是
0.1000000000000000055511151231257827021181583404541015625)所以你通常想要一些舍入.
因此,转换通常涉及近似.良好的转换例程保证在特定(字大小或数字位数)约束内产生最接近的可能近似值.这是大多数复杂性的来源.
看看dtoa.c实施顶部评论中引用的论文,Clinger的" 如何准确读取浮点数",以了解问题的风格; 也许David M. Gay(作者)的论文,正确的圆二进制 - 十进制和十进制 - 二进制转换.
(另外,更一般地说:每个计算机科学家应该知道浮点算术的内容.)
快速浏览一下,可以发现相当多的 C 版本正在处理多个平台,并且该文件看起来可以跨编译器(C 和 C++)、位数、浮点实现和平台通用使用; 具有大量的#define可配置性。
| 归档时间: |
|
| 查看次数: |
10759 次 |
| 最近记录: |