Fast comparison of two doubles ignoring everything that comes after 6 decimal digits

how to compare two double values in c++
comparing doubles in c
c how to compare 2 doubles
safe floating point comparison c++
c float is equal
equality comparison of floating point numbers
comparing floating point numbers
c floating point equals

I try to optimize the performance of some calculation process. Decent amount of time is wasted on calculations like the following:

var isBigger = Math.Abs((long) (a * 1e6) / 1e6D) > ((long) ((b + c) * 1e6)) / 1e6D;

where "a","b" and "c" are doubles, "b" and "c" are positive, "a" might be negative.

isBigger should be true only if absolute value of "a" is bigger than "b+c" disregarding anything after the 6th decimal digit.

So I look at this expression, I understand what it does, but it seems hugely inefficient to me, since it multiplies and divides compared numbers by million just to get rig of anything after 6 decimal places.

Below is the program I used to try and create a better solution. So far I failed. Can someone help me?

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();
        }
    }
}

You don't need the division since if (x/100) < (y/100) that means that x<y.

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

with the results for me:

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. Make sure you compare RELEASE versions of the binary which includes math optimizations.

PS2. The display code is

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}");

Abstract Mathematical Cognition, Fast comparison of two doubles ignoring everything that comes after 6 decimal digits. isBigger should be true only if absolute value of "a" is bigger than "b+c" disregarding anything after the 6th decimal digit. ' Initialize two doubles with apparently identical values Dim double1 As Double = .33333 Dim double2 As Double = 1/3 ' Define the tolerance for variation in their values Dim difference As Double = Math.Abs(double1 * .00001) ' Compare the values ' The output to the console indicates that the two values are equal If Math.Abs(double1 - double2) <= difference Then Console.WriteLine("double1 and

Overall your question goes into the Area of Realtime Programming. Not nessesarily realtime constraint, but it goes into teh same optimisation territory. The kind where every last nanosecond has be shaved off.

.NET is not the ideal scenario for this kind of operation. Usually that thing is done in dedicated lanagauges. The next best thing is doing it in Assembler, C or native C++. .NET has additional features like the Garbage Collector and Just In Time compiler that make even getting reliable benchmark results tricky. Much less reliale runtime performance.

For the datatypes, Float should be about the fastest operation there is. For historical reasons float opeations have been optimized.

One of your comment mentions physics and you do have an array. And I see stuff like array[i] = array2[i] + array3[i]. So maybe this should be a matrix operation you run on the GPU instead? This kind of "huge paralellized array opeartions" is exactly what the GPU is good at. Exactly what drawing on the screen is at it's core.

Unless you tell us what you are actually doing here as sa operation, that is about the best answer I can give.

Experimental Mathematics with Maple, Moreover, children assigned zero the same role as in natural numbers: on the one effect (i.e., faster and less error-prone responses when comparing relatively by simply ignoring the decimal points and then comparing the corresponding property described by unit-decade compatibility of two-digit numbers (Nuerk etal. 1. Compare double – Simple comparison [Not recommended] First look at the simple comparison to understand what exactly is wrong with comparing double with == operator. In given program, I am creating same floating point number (i.e. 1.1) using two methods: Add .1, 11 times. Multiply .1 to 11. In theory, both operations should produce the

Is this what you're looking for?

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

or if you want to know if the difference is bigger (difference either way):

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

(I'm assuming you're not limiting to this precision because of speed but because of inherent floating point limited precision.)

Doubles are not floats, so don't compare them, Franco Vivaldi. (d) Construct the function fp(r) whose value is the fractional part of r. (e) Let QI denote the set of rational numbers lying between 0 and 1. Thus, QI consists of all rationals r = m/n with 0 < m < n. Compute and tabulate Ob for 6 = 2,, 10 [Hint: how do you show that 88 has two decimal digits?] (c) This is the  52 is bigger than 45, so the bigger decimal number is 52.432 We write 52.432 > 45.989 or 45.989 < 52.989 On the other hand, if they are the same, compare the whole number to the right of the decimal point. The smaller decimal number is the one with the smaller whole number on the right of the decimal point. for instance, compare 60.802 with 60.504

In addition to asking this question on this site, I also asked a good friend of mine, and so far he provided the best answer. Here it is:

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

I am happy to have such friends :)

Illustrated Microsoft Office 365 & Excel 2016: Introductory, In some cases, comparing floating-point numbers for exact equality is actually correct. are too quick to pull out an epsilon value is comparing a constant to itself. 20/7 = 2+6/7 (discard integer part, multiply by ten, next digit is two); 60/7 but not all decimal numbers can be exactly represented in binary. Write a method areEqualByThreeDecimalPlaces with two parameters of type double. The method should return boolean and it needs to return true if two double numbers are the same up to three decimal places. Otherwise return false. This would include 3.0 and 3.0 being true, it's only incorrect if the numbers are different up to three decimal places.

Comparing Floating Point Numbers, 2012 Edition, YouY can round a value or formula result to a specific number of decimal places in expenses to show fewer digits; after all, it's not important ts; after all, it' ts; after all, Double-click ROUND in the functions list The new function and an opening to display no decimal places. Compare your worksheet to FIGURE 2-​20. 6. To compare only two decimal numbers between two numbers, you can apply following formulas as you need. Select a blank cell next to the two numbers you want to compare, type this formula =IF((FLOOR(A1, 0.01)-FLOOR(B1, 0.01))=0,1,-1), press Enter key, and to drag fill handle down to compare two columns.

The Journal of Education, That means that comparing two floats to see if they are equal is However it is the next representable float above 1.0, so it is very close (I'm going to ignore the double precision answer), so what do we do? a decimal precision of anywhere from one to over a hundred digits. Thanks for the quick reply! I have got but I need If decimal part is less then 0.5 result contains minus(-) symbol. and if decimal part is greater then equal 0.5 result contains plus (+) symbol. I using following code but It returns all the decimal number with (+) symbol. suppose 456.45 [output should be -.45 ] 456.65 [output should be +.65 ] thanks in advance

Floating point numbers - Manual, And it is worthy the careful perusal of all those who, from lack of experience or upon which the art is founded, are inclined to ignore or depreciate music, as a A child will learn to read, write, and cypher all the faster for giving a little time is generally read thus: “Three, decimal, one, four, one, five, nine, two, six, seven. Excel functions can help you to remove digits after decimal easily. You can do as follows: INT function: In a blank cell besides the value you will remove digits after decimal, enter the formula =INT(E2), and the drag the Fill Handle to the range you need. Notes: (1) In the formula =INT(E2), E2 is the cell that you will remove digits after decimal.

Comments
  • Cutting off precision will not increase performance. If anything the extra rounding step will cost performance. This might be a case where you want fixed precision however (6 digits). For those you can not use double or float. Usuall workarounds include a Fixed Precision Decimal type. or just storing and processing as a Int, only adding the decimal seperate during printout.
  • I also feel compelled to link the Perforamce rant. Because you might be doing micro-optimisations where none are needed: ericlippert.com/2012/12/17/performance-rant
  • Thanks a lot for the linked article! But nope, I come here after I used profiler already, and this kind of calculation is important performance-wise. Regarding cutting precision: this is not the case. These boolean values are calculated to decide the following code flow, ignoring anything that comes after the 6th digit is a result of some physics. (Something falls below the noise)
  • Multiplication and division are really fast nowadays especially when kept as double and the numeric co-processor takes over. What is at least an order of magnitude slower is memory access, so the array lookups may be at fault here. Try to cache the values used in local variables if they are going to be used multiple times.
  • In result1 the Math.Abs() function only applies to arr1[]. Is this correct? Are you checking |a| < (b+c) but with 6 significant digits? Because in your tries you apply the absolute value to all the values | a - (b+c) | and that is different.
  • Does keeping the long cast improve performance still further? Also, the rounding direction is different for negative numbers, so behaviour will be changed for certain cases.
  • Hm... why shouldn't I use cast to long? if I do the following : result2[i] = (long)Math.Abs(arr1[i] * 1e6) > (long)((arr2[i] + arr3[i]) * 1e6); I get even better results... but I am not sure about memory wasted
  • @user3053953 - Really, I would think Math.Floor() would be faster. Also, the treatment of negatives is zero. I am updating my post to remove the Floor() function.
  • * here is a link to online port of this code to Fortran for reference. The speedup over c# on my machine is 5000×.
  • This was the key for the answer. Improved code is in separate post below.
  • Thanks mate, agree with what you say. But, I am not given a lot of time. Just a working code and profiler. Which took me to some function, 40% cpu time used on it, and about 60% of this function time is wasted on 6 calculations like this. So, this is the easiest short-term fix. As for array, there is no such in working code. I used array in test program to actually measure time on something.