我将是第一个承认我对低级编程的总体知识有点稀疏的人。我了解许多核心概念,但我不定期使用它们。话虽如此,我对需要多少代码感到惊讶 DTOA.C.

在过去的几个月中,我一直在C#中进行Ecmascript实施,并且一直在放慢发动机中的孔。昨晚我开始工作 number.prototype.tostring 在节中描述 15.7.4.2eCMAScript规范 (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(),该功能将双重转换为字符串和strtod(),将字符串转换为double。它还包含许多支持功能,其中大多数是为了自身实施任意精神算术。 DTOA.C声名fame的声称是正确的转换,只能通过任意精确的算术来完成这些转换。它还具有可以在四种不同的圆形模式下正确舍入转换的代码。

您的代码仅试图实现与DTOA()的同等相当于,并且由于它使用浮点来进行转换,因此并不总是使它们正确。 (更新:请参阅我的文章 http://www.exploringbinary.com/quick-and-dirty-floation-point-to-decimal-conversion/ 有关详细信息。)

(我在博客上写了很多有关此的文章, http://www.exploringbinary.com/ 。我过去七篇文章中有六篇是仅关于strtod()转换。阅读它们,看看正确进行圆形转换是多么复杂。)

其他提示

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

困难的主要来源是,许多小数分数,即使是简单的小部分,也不是 准确 使用二进制浮点表示 - 例如 0.5 可以(显然),但是 0.1 不能。而且,采用另一种方式(从二进制到十进制),您通常不希望获得绝对准确的结果(例如,最接近的数字的准确小数是 0.1 可以在IEEE-754符合IEEE-754 double 实际上是0.1000000000000000055511151231257827021181583404541015625)因此,您通常想要一些圆形。

因此,转换通常涉及近似。良好的转换程序保证生产 最接近 在特定(单词大小或数字数)约束中可能的近似值。这是大多数复杂性的来源。

看看在评论中引用的论文 dtoa.c 实施,克林格的 如何准确读取浮点数, ,以解决问题的味道;也许大卫·盖伊(作者)的论文, 正确圆形的二进制和十进制二进制转换.

(也更普遍: 每个计算机科学家对浮点算术都应了解什么.)

基于快速浏览一下,大量的C版本正在处理多个平台,例如,该文件似乎是在编译器(C&C ++)(C&C ++),位,浮点实现和平台的跨编译器(C&C ++)中使用的。 ;有很多 #define 可配置。

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

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

这正是销售良好的产品和NIH原型之间的差异。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top