LINQのSum()はOverFlowExceptionが発生することが有りますので、doubleやdecimalなど適切な範囲を持つ数値型を使いましょう、というお話。
var testList1 = new List<int>() { 1, 2147483647 };
//Sumでは型の範囲を超えるとOverflowExceptionが発生
//var sum = testList1.Sum();
//Console.WriteLine("sum = " + sum);
//AggregateすればOverflowExceptionは発生しない。
var aggregate = testList1.Aggregate((sum1, sum2) => sum1 + sum2);
//但し、aggregate = -2147483648になってしまう。
Console.WriteLine("aggregate = " + aggregate);
var doubleSum = testList1.Select(a => (double)a).Sum();
Console.WriteLine("doubleSum = " + doubleSum);//期待通りdoubleSum = 2147483648
var decimalSum = testList1.Select(a => (decimal)a).Sum();
Console.WriteLine("decimalSum = " + decimalSum);//期待通りdecimalSum = 2147483648
事の発端はOpenCVSharpでcontour(OpenCVSharp.Point[])の座標計算でした。
以下の計算をしたところOverFlowExceptionが発生しました。
List<OpenCvSharp.Point> points = GetPoints();
//OverFlowExceptionが発生
points.Select(p => p.X * p.X * p.X - p.X * p.Y * p.Y).Sum();
上のコードは以下数式を演算しようとしました。
\sum_{i=1}^{n}(x_i^3 - x_iy_i^2)
OpenCVSharp.PointのXとYはint型の為、上の計算をintで実行してOverFlowExceptionが発生しました。
渡したパラメータがたまたまintの桁あふれが起きる値で気付けました。
こういった計算で意識せずに桁あふれとか怖いですね。
List<OpenCvSharp.Point> points = GetPoints();
//計算結果が欲しいだけなら
var total = points.Select(p => (double)p.X * (double)p.X * (double)p.X - (double)p.X * (double)p.Y * (double)p.Y).Sum();
//そもそも演算中はdouble型のPoint2dを使ったほうがいいのでは
var toDouble = points.Select(a => new OpenCvSharp.Point2d(a.X,a.Y);