c# - 如何优化 "toExponential"算法的实现以提高精度?

标签 c# algorithm performance implementation ecma262

我觉得我的实现有点幼稚。请注意 max 中的变量 min,它们具有相当小的 +/- 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"));            
}

最佳答案

当您计算 result = i * Math.Pow(10, j - f) - x 时,您试图找到 result 为 0 的位置。由于 jfx都知道,只需要求解i即可。与其编写循环来查找 i,不如说

i * Math.Pow(10, j - f) = x => i = x/Math.Pow(10, j - f)

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

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

关于c# - 如何优化 "toExponential"算法的实现以提高精度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3163102/

相关文章:

c# - 多键数据结构

c# - DocumentDB 分页与排序

c# - Reverse String.Replace - 更快的方法?

c# - 使用 await/async 是否会降低性能?

c# - 如何在 C# 中访问 Silverlight 对象的附加属性?

c# - 如何创建GraphServiceClient?

java - 优化基于网格的粒子系统

algorithm - 为什么这个简单的洗牌算法会产生有偏差的结果?

c++ - 森林的 DFS 算法

java - Java 和 Python 中对 HBase 的并行扫描请求具有不同的性能