如何优化“toExponential”算法的实现以提高精度?

时间:2010-07-02 03:35:06

标签: c# algorithm performance implementation ecma262

我觉得我的实施有点天真。注意min中变量max的精度 +/- 0.0001 。如果我进一步提高精度,代码就太慢了。

算法

alt text http://img35.imageshack.us/img35/2060/toexponential.jpg

代码

private IDynamic ToExponential(Engine engine, Args args)
{
    var x = engine.Context.ThisBinding.ToNumberPrimitive().Value;

    if (double.IsNaN(x))
    {
        return new StringPrimitive("NaN");
    }

    var s = "";

    if (x < 0)
    {
        s = "-";
        x = -x;
    }

    if (double.IsPositiveInfinity(x))
    {
        return new StringPrimitive(s + "Infinity");
    }

    var f = args[0].ToNumberPrimitive().Value;
    if (f < 0D || f > 20D)
    {
        throw new Exception("RangeError");
    }

    var m = "";
    var c = "";
    var d = "";
    var e = 0D;
    var n = 0D;

    if (x == 0D)
    {
        f = 0D;
        m = m.PadLeft((int)(f + 1D), '0');
        e = 0;
    }
    else
    {
        if (!args[0].IsUndefined) // fractionDigits is supplied
        {
            var lower = (int)Math.Pow(10, f);
            var upper = (int)Math.Pow(10, f + 1D);
            var min = 0 - 0.0001;
            var max = 0 + 0.0001;

            for (int i = lower; i < upper; i++)
            {
                for (int j = (int)f; ; --j)
                {
                    var result = i * Math.Pow(10, j - f) - x;
                    if (result > min && result < max)
                    {
                        n = i;
                        e = j;
                        goto Complete;
                    }
                    if (result <= 0)
                    {
                        break;
                    }
                }

                for (int j = (int)f + 1; ; j++)
                {
                    var result = i * Math.Pow(10, j - f) - x;
                    if (result > min && result < max)
                    {
                        n = i;
                        e = j;
                        goto Complete;
                    }
                    if (result >= 0)
                    {
                        break;
                    }
                }
            }
        }
        else
        {
            var min = x - 0.0001;
            var max = x + 0.0001;

            // Scan for f where f >= 0
            for (int i = 0; ; i++)
            {
                // 10 ^ f <= n < 10 ^ (f + 1)
                var lower = (int)Math.Pow(10, i);
                var upper = (int)Math.Pow(10, i + 1D);
                for (int j = lower; j < upper; j++)
                {
                    // n is not divisible by 10
                    if (j % 10 == 0)
                    {
                        continue;
                    }

                    // n must have f + 1 digits
                    var digits = 0;
                    var state = j;
                    while (state > 0)
                    {
                        state /= 10;
                        digits++;
                    }
                    if (digits != i + 1)
                    {
                        continue;
                    }

                    // Scan for e in both directions
                    for (int k = (int)i; ; --k)
                    {
                        var result = j * Math.Pow(10, k - i);
                        if (result > min && result < max)
                        {
                            f = i;
                            n = j;
                            e = k;
                            goto Complete;
                        }
                        if (result <= i)
                        {
                            break;
                        }
                    }
                    for (int k = (int)i + 1; ; k++)
                    {
                        var result = i * Math.Pow(10, k - i);
                        if (result > min && result < max)
                        {
                            f = i;
                            n = j;
                            e = k;
                            goto Complete;
                        }
                        if (result >= i)
                        {
                            break;
                        }
                    }
                }
            }
        }

    Complete:

        m = n.ToString("G");
    }

    if (f != 0D)
    {
        m = m[0] + "." + m.Substring(1);
    }

    if (e == 0D)
    {
        c = "+";
        d = "0";
    }
    else
    {
        if (e > 0D)
        {
            c = "+";
        }
        else
        {
            c = "-";
            e = -e;
        }
        d = e.ToString("G");
    }

    m = m + "e" + c + d;
    return new StringPrimitive(s + m);
}

最终版本

我发誓当我最初写这篇文章时,有人必须用特别大的锤子击打我......

private IDynamic ToExponential(Engine engine, Args args)
{
    var x = engine.Context.ThisBinding.ToNumberPrimitive().Value;

    if (args[0].IsUndefined)
    {
        return new StringPrimitive(x.ToString("0.####################e+0"));
    }

    var f = args[0].ToNumberPrimitive().Value;

    if (f < 0D || f > 20D)
    {
        RuntimeError.RangeError("The parameter fractionDigits must be between 0 and 20.");
    }

    return new StringPrimitive(x.ToString("0." + string.Empty.PadRight((int)f, '0') + "e+0"));            
}

2 个答案:

答案 0 :(得分:1)

我首先使用Math.Log10而不是循环来查找指数。

答案 1 :(得分:1)

当您计算result = i * Math.Pow(10, j - f) - x时,您试图找到result为0的位置。由于jfx已知,您只需知道需要解决i。您可以说

,而不是编写循环来查找i

i * Math.Pow(10, j - f) = x =&gt; i = x / Math.Pow(10, j - f)

您需要的值应该是floor(i)ceil(i)

出于好奇,您是否检查过ToString("e")是否给出了正确答案?