Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correctly implement MOD function #407

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions ClosedXML/Excel/CalcEngine/CalcEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -413,11 +413,11 @@ private Expression ParseAtom()
var pCnt = p == null ? 0 : p.Count;
if (fnDef.ParmMin != -1 && pCnt < fnDef.ParmMin)
{
Throw("Too few parameters.");
Throw(string.Format("Too few parameters for function '{0}'. Expected a minimum of {1} and a maximum of {2}.", id, fnDef.ParmMin, fnDef.ParmMax));
}
if (fnDef.ParmMax != -1 && pCnt > fnDef.ParmMax)
{
Throw("Too many parameters.");
Throw(string.Format("Too many parameters for function '{0}'.Expected a minimum of {1} and a maximum of {2}.", id, fnDef.ParmMin, fnDef.ParmMax));
}
x = new FunctionExpression(fnDef, p);
break;
Expand Down
79 changes: 51 additions & 28 deletions ClosedXML/Excel/CalcEngine/Functions/MathTrig.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using ClosedXML.Excel.CalcEngine.Functions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ClosedXML.Excel.CalcEngine.Functions;

namespace ClosedXML.Excel.CalcEngine
{
Expand All @@ -29,7 +29,8 @@ public static void Register(CalcEngine ce)
ce.RegisterFunction("EXP", 1, Exp);
ce.RegisterFunction("FACT", 1, Fact);
ce.RegisterFunction("FACTDOUBLE", 1, FactDouble);
ce.RegisterFunction("FLOOR", 1, Floor);
ce.RegisterFunction("FLOOR", 1, 2, Floor);
//ce.RegisterFunction("FLOOR.MATH", 1, 3, FloorMath);
ce.RegisterFunction("GCD", 1, 255, Gcd);
ce.RegisterFunction("INT", 1, Int);
ce.RegisterFunction("LCM", 1, 255, Lcm);
Expand Down Expand Up @@ -120,12 +121,27 @@ private static object Exp(List<Expression> p)

private static object Floor(List<Expression> p)
{
return Math.Floor(p[0]);
double number = p[0];
double significance = 1;
if (p.Count > 1)
significance = p[1];

if (significance < 0)
{
number = -number;
significance = -significance;

return -Math.Floor(number / significance) * significance;
}
else if (significance == 1)
return Math.Floor(number);
else
return Math.Floor(number / significance) * significance;
}

private static object Int(List<Expression> p)
{
return (int) ((double) p[0]);
return (int)((double)p[0]);
}

private static object Ln(List<Expression> p)
Expand All @@ -135,7 +151,7 @@ private static object Ln(List<Expression> p)

private static object Log(List<Expression> p)
{
var lbase = p.Count > 1 ? (double) p[1] : 10;
var lbase = p.Count > 1 ? (double)p[1] : 10;
return Math.Log(p[0], lbase);
}

Expand All @@ -161,7 +177,7 @@ private static object Rand(List<Expression> p)

private static object RandBetween(List<Expression> p)
{
return _rnd.Next((int) (double) p[0], (int) (double) p[1]);
return _rnd.Next((int)(double)p[0], (int)(double)p[1]);
}

private static object Sign(List<Expression> p)
Expand Down Expand Up @@ -240,42 +256,42 @@ private static object Tanh(List<Expression> p)

private static object Trunc(List<Expression> p)
{
return (double) (int) ((double) p[0]);
return (double)(int)((double)p[0]);
}

public static double DegreesToRadians(double degrees)
{
return (Math.PI/180.0)*degrees;
return (Math.PI / 180.0) * degrees;
}

public static double RadiansToDegrees(double radians)
{
return (180.0/Math.PI)*radians;
return (180.0 / Math.PI) * radians;
}

public static double GradsToRadians(double grads)
{
return (grads/200.0)*Math.PI;
return (grads / 200.0) * Math.PI;
}

public static double RadiansToGrads(double radians)
{
return (radians/Math.PI)*200.0;
return (radians / Math.PI) * 200.0;
}

public static double DegreesToGrads(double degrees)
{
return (degrees/9.0)*10.0;
return (degrees / 9.0) * 10.0;
}

public static double GradsToDegrees(double grads)
{
return (grads/10.0)*9.0;
return (grads / 10.0) * 9.0;
}

public static double ASinh(double x)
{
return (Math.Log(x + Math.Sqrt(x*x + 1.0)));
return (Math.Log(x + Math.Sqrt(x * x + 1.0)));
}

private static object Acosh(List<Expression> p)
Expand All @@ -295,8 +311,8 @@ private static object Atanh(List<Expression> p)

private static object Combin(List<Expression> p)
{
Int32 n = (int) p[0];
Int32 k = (int) p[1];
Int32 n = (int)p[0];
Int32 k = (int)p[1];
return XLMath.Combin(n, k);
}

Expand All @@ -305,8 +321,6 @@ private static object Degrees(List<Expression> p)
return p[0] * (180.0 / Math.PI);
}



private static object Fact(List<Expression> p)
{
var num = Math.Floor(p[0]);
Expand Down Expand Up @@ -348,15 +362,15 @@ private static object Lcm(List<Expression> p)
private static int Lcm(int a, int b)
{
if (a == 0 || b == 0) return 0;
return a * ( b / Gcd(a, b));
return a * (b / Gcd(a, b));
}

private static object Mod(List<Expression> p)
{
Int32 n = (int)Math.Abs(p[0]);
Int32 d = (int)p[1];
var ret = n % d;
return d < 0 ? ret * -1 : ret;
double number = p[0];
double divisor = p[1];

return number - Math.Floor(number / divisor) * divisor;
}

private static object MRound(List<Expression> p)
Expand Down Expand Up @@ -479,7 +493,6 @@ private static object Round(List<Expression> p)
temp = Math.Round(temp, 0, MidpointRounding.AwayFromZero);
return temp * Math.Pow(10, digits);
}

}

private static object RoundDown(List<Expression> p)
Expand Down Expand Up @@ -512,7 +525,7 @@ private static object SeriesSum(List<Expression> p)
var obj = p[3] as XObjectExpression;

if (obj == null)
return p[3] * Math.Pow(x , n);
return p[3] * Math.Pow(x, n);

Double total = 0;
Int32 i = 0;
Expand Down Expand Up @@ -540,26 +553,37 @@ private static object Subtotal(List<Expression> p)
{
case 1:
return tally.Average();

case 2:
return tally.Count(true);

case 3:
return tally.Count(false);

case 4:
return tally.Max();

case 5:
return tally.Min();

case 6:
return tally.Product();

case 7:
return tally.Std();

case 8:
return tally.StdP();

case 9:
return tally.Sum();

case 10:
return tally.Var();

case 11:
return tally.VarP();

default:
throw new ArgumentException("Function not supported.");
}
Expand Down Expand Up @@ -591,19 +615,18 @@ private static object MMult(List<Expression> p)
}
}


return C;
}

private static double[,] GetArray(Expression expression)
{
var oExp1 = expression as XObjectExpression;
if (oExp1 == null) return new [,]{{(Double)expression}};
if (oExp1 == null) return new[,] { { (Double)expression } };

var range = (oExp1.Value as CellRangeReference).Range;
var rowCount = range.RowCount();
var columnCount = range.ColumnCount();
var arr = new double[rowCount,columnCount];
var arr = new double[rowCount, columnCount];

for (int row = 0; row < rowCount; row++)
{
Expand Down
1 change: 1 addition & 0 deletions ClosedXML_Tests/ClosedXML_Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
<Compile Include="Excel\CalcEngine\InformationTests.cs" />
<Compile Include="Excel\CalcEngine\LogicalTests.cs" />
<Compile Include="Excel\CalcEngine\LookupTests.cs" />
<Compile Include="Excel\CalcEngine\MathTrigTests.cs" />
<Compile Include="Excel\CalcEngine\StatisticalTests.cs" />
<Compile Include="Excel\CalcEngine\TextTests.cs" />
<Compile Include="Excel\Comments\CommentsTests.cs" />
Expand Down
102 changes: 102 additions & 0 deletions ClosedXML_Tests/Excel/CalcEngine/MathTrigTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using ClosedXML.Excel;
using NUnit.Framework;
using System;

namespace ClosedXML_Tests.Excel.CalcEngine
{
[TestFixture]
public class MathTrigTests
{
private readonly double tolerance = 1e-10;

[Test]
public void Floor()
{
Object actual;

actual = XLWorkbook.EvaluateExpr(@"FLOOR(1.2)");
Assert.AreEqual(1, actual);

actual = XLWorkbook.EvaluateExpr(@"FLOOR(1.7)");
Assert.AreEqual(1, actual);

actual = XLWorkbook.EvaluateExpr(@"FLOOR(-1.7)");
Assert.AreEqual(-2, actual);

actual = XLWorkbook.EvaluateExpr(@"FLOOR(1.2, 1)");
Assert.AreEqual(1, actual);

actual = XLWorkbook.EvaluateExpr(@"FLOOR(1.7, 1)");
Assert.AreEqual(1, actual);

actual = XLWorkbook.EvaluateExpr(@"FLOOR(-1.7, 1)");
Assert.AreEqual(-2, actual);

actual = XLWorkbook.EvaluateExpr(@"FLOOR(0.4, 2)");
Assert.AreEqual(0, actual);

actual = XLWorkbook.EvaluateExpr(@"FLOOR(2.7, 2)");
Assert.AreEqual(2, actual);

actual = XLWorkbook.EvaluateExpr(@"FLOOR(7.8, 2)");
Assert.AreEqual(6, actual);

actual = XLWorkbook.EvaluateExpr(@"FLOOR(-5.5, -2)");
Assert.AreEqual(-4, actual);
}

//[Test]
// Functions have to support a period first before we can implement this
public void FloorMath()
{
Object actual;

actual = XLWorkbook.EvaluateExpr(@"FLOOR.MATH(24.3, 5)");
Assert.AreEqual(20, actual);

actual = XLWorkbook.EvaluateExpr(@"FLOOR.MATH(6.7)");
Assert.AreEqual(6, actual);

actual = XLWorkbook.EvaluateExpr(@"FLOOR.MATH(-8.1, 2)");
Assert.AreEqual(-10, actual);

actual = XLWorkbook.EvaluateExpr(@"FLOOR.MATH(-5.5, 2, -1)");
Assert.AreEqual(-4, actual);
}

[Test]
public void Mod()
{
double actual;

actual = (double)XLWorkbook.EvaluateExpr(@"MOD(1.5, 1)");
Assert.AreEqual(0.5, actual, tolerance);

actual = (double)XLWorkbook.EvaluateExpr(@"MOD(3, 2)");
Assert.AreEqual(1, actual, tolerance);

actual = (double)XLWorkbook.EvaluateExpr(@"MOD(-3, 2)");
Assert.AreEqual(1, actual, tolerance);

actual = (double)XLWorkbook.EvaluateExpr(@"MOD(3, -2)");
Assert.AreEqual(-1, actual, tolerance);

actual = (double)XLWorkbook.EvaluateExpr(@"MOD(-3, -2)");
Assert.AreEqual(-1, actual, tolerance);

//////

actual = (double)XLWorkbook.EvaluateExpr(@"MOD(-4.3, -0.5)");
Assert.AreEqual(-0.3, actual, tolerance);

actual = (double)XLWorkbook.EvaluateExpr(@"MOD(6.9, -0.2)");
Assert.AreEqual(-0.1, actual, tolerance);

actual = (double)XLWorkbook.EvaluateExpr(@"MOD(0.7, 0.6)");
Assert.AreEqual(0.1, actual, tolerance);

actual = (double)XLWorkbook.EvaluateExpr(@"MOD(6.2, 1.1)");
Assert.AreEqual(0.7, actual, tolerance);
}
}
}