快速比较两个双精度数,忽略6位十进制数字之后的所有内容

时间:2018-12-04 20:24:19

标签: c# performance double precision

我尝试优化某些计算过程的性能。 像下面这样的计算浪费了大量时间:

class Program
{
    static void Main(string[] args)
    {
        var arrLength = 1000000;
        var arr1 = GetArrayOf_A(arrLength);
        var arr2 = GetArrayOf_B(arrLength);
        var arr3 = GetArrayOf_C(arrLength);
        var result1 = new bool[arrLength];
        var result2 = new bool[arrLength];


        var sw = new Stopwatch();

        sw.Start();
        for (var i = 0; i < arrLength; i++)
        {
            result1[i] = Math.Abs((long) (arr1[i] * 1e6) / 1e6D) 
                         > 
                         (long) ((arr2[i] + arr3[i]) * 1e6) / 1e6D;
        }
        sw.Stop();
        var t1 = sw.Elapsed.TotalMilliseconds;

        sw.Restart();
        for (var i = 0; i < arrLength; i++)
        {
            //result2[i] = Math.Round(Math.Abs(arr1[i]) - (arr2[i] + arr3[i]),6) > 0; // Incorrect, example by index = 0
            //result2[i] = Math.Abs(arr1[i]) - (arr2[i] + arr3[i]) > 0.000001; // Incorrect, example by index = 1
            //result2[i] = Math.Abs(arr1[i]) - (arr2[i] + arr3[i]) > 0.0000001; // Incorrect, example by index = 2
            result2[i] = Math.Abs(arr1[i]) - (arr2[i] + arr3[i]) > 0.00000001; // Incorrect, example by index = 3
        }
        sw.Stop();
        var t2 = sw.Elapsed.TotalMilliseconds;

        var areEquivalent = true;
        for (var i = 0; i < arrLength; i++)
        {
            if (result1[i] == result2[i]) continue;

            areEquivalent = false;
            break;
        }

        Console.WriteLine($"Functions are equivalent : {areEquivalent}");
        if (areEquivalent)
        {
            Console.WriteLine($"Current function total time: {t1}ms");
            Console.WriteLine($"Equivalent function total time: {t2}ms");
        }

        Console.WriteLine("Press ANY key to quit . . .");
        Console.ReadKey();
    }

    private static readonly Random _rand = new Random(DateTime.Now.Millisecond);
    private const int NumberOfRepresentativeExamples = 4;

    private static double[] GetArrayOf_A(int arrLength)
    {
        if(arrLength<=NumberOfRepresentativeExamples) 
            throw new ArgumentException($"{nameof(arrLength)} should be bigger than {NumberOfRepresentativeExamples}");

        var arr = new double[arrLength];

        // Representative numbers
        arr[0] = 2.4486382579120365;
        arr[1] = -1.1716818990000011;
        arr[2] = 5.996414627393257;
        arr[3] = 6.0740085822069;

        // the rest is to build time statistics
        FillTheRestOfArray(arr);

        return arr;
    }
    private static double[] GetArrayOf_B(int arrLength)
    {
        if(arrLength<=NumberOfRepresentativeExamples) 
            throw new ArgumentException($"{nameof(arrLength)} should be bigger than {NumberOfRepresentativeExamples}");

        var arr = new double[arrLength];

        // Representative numbers
        arr[0] = 2.057823225;
        arr[1] = 0;
        arr[2] = 2.057823225;
        arr[3] = 2.060649901;

        // the rest is to build time statistics
        FillTheRestOfArray(arr);

        return arr;
    }
    private static double[] GetArrayOf_C(int arrLength)
    {
        if(arrLength<=NumberOfRepresentativeExamples) 
            throw new ArgumentException($"{nameof(arrLength)} should be bigger than {NumberOfRepresentativeExamples}");

        var arr = new double[arrLength];

        // Representative numbers
        arr[0] = 0.3908145999796302;
        arr[1] = 1.1716809269999997;
        arr[2] = 3.9385910820740282;
        arr[3] = 4.0133582670728858;

        // the rest is to build time statistics
        FillTheRestOfArray(arr);

        return arr;
    }
    private static void FillTheRestOfArray(double[] arr)
    {
        for (var i = NumberOfRepresentativeExamples; i < arr.Length; i++)
        {
            arr[i] = _rand.Next(0, 10) + _rand.NextDouble();
        }
    }
}

其中“ a”,“ b”和“ c”为双精度,“ b”和“ c”为正,“ a”可能为负。

isBigger仅在“ a”的绝对值大于“ b + c”而不考虑小数点后第六位的任何情况下才为真。

所以我看一下这个表达式,我理解它的作用,但是对我来说似乎效率很低,因为它会将比较数字乘以百万并除以仅仅得到小数点后6位。

下面是我用来尝试创建更好解决方案的程序。到目前为止,我失败了。 有人可以帮我吗?

{{1}}

4 个答案:

答案 0 :(得分:1)

您不需要除法,因为如果(x/100) < (y/100)表示x<y

for(var i = 0; i < arrLength; i++)
{
    result2[i] = Math.Abs((long)(arr1[i] * 1e6)) 
                    > (long)((arr2[i] + arr3[i]) * 1e6);
}

显示给我的结果

Arrays have 1000000 elements.
Functions are equivalent : True
   Current function total time: 40.10ms 24.94 kflop
Equivalent function total time: 22.42ms 44.60 kflop
A speedup of 78.83 %

PS。确保比较包含数学优化功能的二进制文件的 RELEASE 版本。

PS2。显示代码是

Console.WriteLine($"Arrays have {arrLength} elements.");
Console.WriteLine($"Functions are equivalent : {areEquivalent}");
Console.WriteLine($"   Current function total time: {t1:F2}ms {arrLength/t1/1e3:F2} kflop");
Console.WriteLine($"Equivalent function total time: {t2:F2}ms {arrLength/t2/1e3:F2} kflop");
Console.WriteLine($"An speedup of {t1/t2-1:P2}");

答案 1 :(得分:1)

总体而言,您的问题属于实时编程领域。不一定是实时约束,但它会进入相同的优化范围。最后一纳秒的时间被刮掉的类型。

.NET不是这种操作的理想方案。通常,这件事是在专用的挂绳上完成的。接下来的最好的事情是在Assembler,C或本机C ++中执行此操作。 .NET具有其他功能,例如垃圾收集器和即时编译器,这些功能甚至使获取可靠的基准测试结果也变得棘手。可靠的运行时性能要差得多。

对于数据类型,Float应该是最快的操作。由于历史原因,对浮动操作进行了优化。

您的评论中有一项提到物理,并且确实有一个数组。我看到诸如array[i] = array2[i] + array3[i]之类的东西。因此,也许这应该是您在GPU上运行的矩阵运算?这种“巨大的并行阵列阵列选择”正是GPU擅长的。

除非您告诉我们您在进行sa操作时实际上在做什么,否则这就是我能给出的最佳答案。

答案 2 :(得分:0)

这是您要找的吗?

Math.Abs(a) - (b + c) > 0.000001

或者您是否想知道差异是否更大(两种方式之间的差异):

Math.Abs(Math.Abs(a) - (b + c)) > 0.000001

(我假设您不是因为速度而限制此精度,而是因为固有的浮点数限制精度。)

答案 3 :(得分:0)

除了在此站点上问这个问题外,我还问了我的一个好朋友,到目前为止,他提供了最佳答案。在这里:

result2[i] = Math.Abs(arr1[i]) - (arr2[i] + arr3[i]) > 0.000001 || 
             Math.Abs((long)(arr1[i] * 1e6)) > (long)((arr2[i] + arr3[i])*1e6);

我很高兴有这样的朋友:)