• Home
  • Jobs
  • Courses
  • Questions
  • Teachers
  • For business
  • ES/EN

0

103
Views
C#/LINQ Taking Sum of Decimals show different results

I have a list of objects that I am attempting to take sum of after multiple 3 of the fields by each other. The problem is that the final result is NOT matching when I run the calculation by the following 2 cases:

1 - purchasesDeserialized.Sum(reference => reference.Price * reference.Box * reference.Qty)

2 - purchasesDeserialized.OrderBy(r => r.Box).Sum(reference => reference.Price * reference.Box * reference.Qty);

The data is identical in two cases, difference is in case #1 I do the calculation without sorting first vs in case #2 I first sort then calculate. (I expected the outcome to be the same since the sorting should not change any underlying data but rather just reorder them).

Not sure if LINQ is affecting the calculation after the OrderBy or if the issue lands on the C# decimal rounding side of things.

Full replicating code:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    public class Purchase
    {
        public decimal Price { get; set; }
        public decimal Box { get; set; }
        public decimal Qty { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {

            string purchases = "[{\"Box\":10.0,\"Qty\":206.000000,\"Price\":8.323300970873786407766990292},{\"Box\":10.0,\"Qty\":108.000000,\"Price\":8.333333333333333333333333333},{\"Box\":10.0,\"Qty\":46.000000,\"Price\":8.695652173913043478260869565},{\"Box\":10.0,\"Qty\":18.000000,\"Price\":24.833333333333333333333333333},{\"Box\":1.0,\"Qty\":566.000000,\"Price\":80.87985865724381625441696112},{\"Box\":1.0,\"Qty\":12.000000,\"Price\":97.46666666666666666666666667},{\"Box\":1.0,\"Qty\":72.000000,\"Price\":103.06805555555555555555555556},{\"Box\":1.0,\"Qty\":246.000000,\"Price\":81.2906504065040650406504065},{\"Box\":1.0,\"Qty\":78.000000,\"Price\":80.08333333333333333333333333},{\"Box\":10.0,\"Qty\":146.000000,\"Price\":8.030821917808219178082191782},{\"Box\":10.0,\"Qty\":178.000000,\"Price\":8.326404494382022471910112359},{\"Box\":10.0,\"Qty\":364.000000,\"Price\":8.324175824175824175824175825},{\"Box\":10.0,\"Qty\":30.000000,\"Price\":8.666666666666666666666666667},{\"Box\":10.0,\"Qty\":36.000000,\"Price\":24.5000000000000000000},{\"Box\":1.0,\"Qty\":120.000000,\"Price\":83.662500000000000000},{\"Box\":1.0,\"Qty\":332.000000,\"Price\":80.74698795180722891566265061},{\"Box\":1.0,\"Qty\":36.000000,\"Price\":78.833333333333333333333333333},{\"Box\":1.0,\"Qty\":22.000000,\"Price\":96.35909090909090909090909091},{\"Box\":1.0,\"Qty\":134.000000,\"Price\":78.149253731343283582089552239},{\"Box\":10.0,\"Qty\":26.000000,\"Price\":24.346153846153846153846153846},{\"Box\":1.0,\"Qty\":298.000000,\"Price\":97.06644295302013422818791947},{\"Box\":1.0,\"Qty\":18.000000,\"Price\":95.22777777777777777777777778},{\"Box\":10.0,\"Qty\":6.000000,\"Price\":24.166666666666666666666666667},{\"Box\":1.0,\"Qty\":82.000000,\"Price\":96.42195121951219512195121951},{\"Box\":10.0,\"Qty\":154.000000,\"Price\":8.149350649350649350649350649}]";
            var purchasesDeserialized = JsonConvert.DeserializeObject<List<Purchase>>(purchases);

            var sumRes1 = purchasesDeserialized
                .Sum(reference => reference.Price * reference.Box * reference.Qty);
            Console.WriteLine("Sum:" + sumRes1); //returns 294648.40000000000000000000000M

            var sumRes2 = purchasesDeserialized
                .OrderBy(r => r.Box)
               .Sum(reference => reference.Price * reference.Box * reference.Qty);
            Console.WriteLine("Sum after sort:" + sumRes2); //returns 294648.39999999999999999999999M
        }
    }
}

And the output:

Sum:294648.40000000000000000000000
Sum after sort:294648.39999999999999999999999
5 months ago ·

Santiago Trujillo

2 answers
Answer question

0

I think the issue is as you are increasing the value of the total amount (during the Sum operation) at a same time you are decreasing the decimal maximum precision.

In this case the order of elements in the collection matters because it will affect at which point you exceed the decimal precision. The remaining elements then will be added but not precisely which in the end results in different totals.

To give an example, lets say my data type can hold up to 4 places:

var x = 4.998;
var y = 0.002;
var z = 10.00;

x + y + z = 4.998 + 0.002 + 10.00 => 15.00;
z + x + y = 10.00 + 4.998 + 0.002 => 14.99; //(because 10.00 + 4.998 = 14.99 and there is no precision left for remaining decimal place, so its stripped)
5 months ago · Santiago Trujillo Report

0

Consider for example the terms indexed from your json 0 and 22

decimal d00 = 10M * 206.000000M * 8.323300970873786407766990292M;
decimal d22 = 10.0M * 6.000000M * 24.166666666666666666666666667M;
decimal sum = d00 + d22;

The actual results are (found using a full precision calculator)

d00 = 17146.00000000000000000000000152 // 31
d22 =  1450.00000000000000000000000002 // 30
sum = 18596.00000000000000000000000154 // 31

but you will lose precision as you see that d00 should have 31 total digits, etc., and being limited to decimal precision, the results in c# are

d00 = 17146.000000000000000000000002  // 29
d22 =  1450.0000000000000000000000000 // 29
sum = 18596.000000000000000000000002  // 29

so the error in this arbitrary sum is 0.00000000000000000000000046. With the way rounding goes, the errors will round up or down, depending on the order in which the addition operations go (50% chance of rounding one way or another at each step), so you will most likely end up with different results from different orders.

5 months ago · Santiago Trujillo Report
Answer question
Find remote jobs
Loading

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post job Plans Our process Sales
Legal
Terms and conditions Privacy policy
© 2022 PeakU Inc. All Rights Reserved.