Browse Source

Precision: RoundToMultiple, RoundToPower

v4
Christoph Ruegg 6 years ago
parent
commit
7cf756e841
  1. 52
      src/Numerics.Tests/PrecisionTest.cs
  2. 184
      src/Numerics/Precision.cs

52
src/Numerics.Tests/PrecisionTest.cs

@ -1172,13 +1172,13 @@ namespace MathNet.Numerics.UnitTests
}
[Test]
[TestCase(100.3123, -3, 0)]
[TestCase(1100.6123, -3, 1000)]
[TestCase(1500.8123, -3, 2000)]
[TestCase(110003245.3123, -3, 110003000)]
[TestCase(110003245.3123, -1, 110003250)]
[TestCase(110003245.3123, 2, 110003245.31)]
[TestCase(110003245.3123, 0, 110003245)]
[TestCase(100.3123d, -3, 0d)]
[TestCase(1100.6123d, -3, 1000d)]
[TestCase(1500.8123d, -3, 2000d)]
[TestCase(110003245.3123d, -3, 110003000d)]
[TestCase(110003245.3123d, -1, 110003250d)]
[TestCase(110003245.3123d, 2, 110003245.31d)]
[TestCase(110003245.3123d, 0, 110003245d)]
public void RoundDouble(double number, int digit, double expectedResult)
{
Assert.AreEqual(expectedResult,number.Round(digit));
@ -1212,19 +1212,18 @@ namespace MathNet.Numerics.UnitTests
[Test]
public void RoundUint32()
{
Assert.AreEqual(110003250,((uint)110003245).Round(-1));
Assert.AreEqual(110003250U,110003245U.Round(-1));
}
[Test]
public void RoundUInt64()
{
Assert.AreEqual(3250, ((ulong)3245).Round(-1));
Assert.AreEqual(3250UL, 3245UL.Round(-1));
}
[Test]
[TestCase(110003245, -1, 110003250)]
public void RoundInt64(long number, int digit, int expectedResult)
[TestCase(110003245L, -1, 110003250L)]
public void RoundInt64(long number, int digit, long expectedResult)
{
Assert.AreEqual(expectedResult, number.Round(digit));
}
@ -1252,5 +1251,34 @@ namespace MathNet.Numerics.UnitTests
Assert.AreEqual(new BigInteger(110003245), new BigInteger(110003245).Round(2));
Assert.AreEqual(new BigInteger(110003245), new BigInteger(110003245).Round(0));
}
[Test]
[TestCase(0.0, 2.0, 0.0)]
[TestCase(0.1, 2.0, 0.125)]
[TestCase(0.7, 2.0, 0.5)]
[TestCase(0.8, 2.0, 1.0)]
[TestCase(1.1, 2.0, 1.0)]
[TestCase(1.6, 2.0, 2.0)]
[TestCase(3.2, 2.0, 4.0)]
[TestCase(14.0, 2.0, 16.0)]
[TestCase(-14.0, 2.0, -16.0)]
public void RoundToPowerDouble(double number, double basis, double expectedResult)
{
Assert.AreEqual(expectedResult, number.RoundToPower(basis), 0.0);
}
[Test]
[TestCase(0.0, 0.3, 0.0)]
[TestCase(0.1, 0.3, 0.0)]
[TestCase(0.2, 0.3, 0.3)]
[TestCase(0.8, 0.3, 0.9)]
[TestCase(1.1, 0.3, 1.2)]
[TestCase(1.6, 0.3, 1.5)]
[TestCase(3.2, 0.3, 3.3)]
[TestCase(-3.2, 0.3, -3.3)]
public void RoundToMultipleDouble(double number, double basis, double expectedResult)
{
Assert.AreEqual(expectedResult, number.RoundToMultiple(basis), Precision.DoublePrecision);
}
}
}

184
src/Numerics/Precision.cs

@ -746,7 +746,74 @@ namespace MathNet.Numerics
}
/// <summary>
/// Round to the number closest to 10^(-decimals). Supports negative decimals to round within the integer part.
/// Calculates the actual positive double precision machine epsilon - the smallest number that can be added to 1, yielding a results different than 1.
/// This is also known as unit roundoff error. According to the definition of Prof. Higham.
/// </summary>
/// <returns>Machine epsilon</returns>
static double MeasurePositiveMachineEpsilon()
{
double eps = 1.0d;
while ((1.0d + (eps / 2.0d)) > 1.0d)
eps /= 2.0d;
return eps;
}
/// <summary>
/// Round to a multiple of the provided positive basis.
/// </summary>
/// <param name="number">Number to be rounded.</param>
/// <param name="basis">The basis to whose multiples to round to. Must be positive.</param>
public static double RoundToMultiple(this double number, double basis)
{
return Math.Round(number / basis, MidpointRounding.AwayFromZero) * basis;
}
/// <summary>
/// Round to a multiple of the provided positive basis.
/// </summary>
/// <param name="number">Number to be rounded.</param>
/// <param name="basis">The basis to whose multiples to round to. Must be positive.</param>
public static float RoundToMultiple(this float number, float basis)
{
return (float) RoundToMultiple((double) number, basis);
}
/// <summary>
/// Round to a multiple of the provided positive basis.
/// </summary>
/// <param name="number">Number to be rounded.</param>
/// <param name="basis">The basis to whose multiples to round to. Must be positive.</param>
public static decimal RoundToMultiple(this decimal number, decimal basis)
{
return Math.Round(number / basis, MidpointRounding.AwayFromZero) * basis;
}
/// <summary>
/// Round to a multiple of the provided positive basis.
/// </summary>
/// <param name="number">Number to be rounded.</param>
/// <param name="basis">The basis to whose powers to round to. Must be positive.</param>
public static double RoundToPower(this double number, double basis)
{
return number < 0.0
? -Math.Pow(basis, Math.Round(Math.Log(-number, basis), MidpointRounding.AwayFromZero))
: Math.Pow(basis, Math.Round(Math.Log(number, basis), MidpointRounding.AwayFromZero));
}
/// <summary>
/// Round to a multiple of the provided positive basis.
/// </summary>
/// <param name="number">Number to be rounded.</param>
/// <param name="basis">The basis to whose powers to round to. Must be positive.</param>
public static float RoundToPower(this float number, float basis)
{
return (float) RoundToPower((double) number, basis);
}
/// <summary>
/// Round to the number closest to 10^(-decimals). Negative decimals to round within the integer part.
/// </summary>
/// <param name="number">Number to be rounded</param>
/// <param name="decimals">Number of decimals to round to. Negative to round within the integer part, e.g. -3 will wound to the closes 1000.</param>
@ -754,16 +821,13 @@ namespace MathNet.Numerics
/// <returns>Rounded number</returns>
public static double Round(this double number, int decimals)
{
if (decimals >= 0)
{
return Math.Round(number, decimals, MidpointRounding.AwayFromZero);
}
return Math.Round(number / Math.Pow(10, -decimals), MidpointRounding.AwayFromZero) * Math.Pow(10, -decimals);
return decimals >= 0
? Math.Round(number, decimals, MidpointRounding.AwayFromZero)
: RoundToMultiple(number, Math.Pow(10.0, -decimals));
}
/// <summary>
/// Round to the number closest to 10^(-decimals). Supports negative decimals to round within the integer part.
/// Round to the number closest to 10^(-decimals). Negative decimals to round within the integer part.
/// </summary>
/// <param name="number">Number to be rounded</param>
/// <param name="decimals">Number of decimals to round to. Negative to round within the integer part, e.g. -3 will wound to the closes 1000.</param>
@ -771,46 +835,39 @@ namespace MathNet.Numerics
/// <returns>Rounded number</returns>
public static float Round(this float number, int decimals)
{
return (float) Round((decimal) number, decimals);
return (float) Round((double) number, decimals);
}
/// <summary>
/// Round to the number closest to 10^(-decimals). Supports negative decimals to round within the integer part.
/// Round to the number closest to 10^(-decimals). Negative decimals to round within the integer part.
/// </summary>
/// <param name="number">Number to be rounded</param>
/// <param name="decimals">Number of decimals to round to. Negative to round within the integer part, e.g. -3 will wound to the closes 1000.</param>
/// <example>To round 123456789 to hundreds Round(123456789, -2) = 123456800 </example>
/// <returns>Rounded number</returns>
public static decimal Round(this decimal number, int decimals)
{
if (decimals >= 0)
{
return Math.Round(number, decimals, MidpointRounding.AwayFromZero);
}
decimal roundTo = (decimal) Math.Pow(10, -decimals);
return Math.Round(number / roundTo, MidpointRounding.AwayFromZero) * roundTo;
{
return decimals >= 0
? Math.Round(number, decimals, MidpointRounding.AwayFromZero)
: RoundToMultiple(number, (decimal) Math.Pow(10.0, -decimals));
}
/// <summary>
/// Round to the number closest to 10^(-decimals). Supports negative decimals to round within the integer part.
/// Round to the number closest to 10^(-decimals). Negative decimals to round within the integer part.
/// </summary>
/// <param name="number">Number to be rounded</param>
/// <param name="decimals">Number of decimals to round to. Negative to round within the integer part, e.g. -3 will wound to the closes 1000.</param>
/// <example>To round 123456789 to hundreds Round(123456789, -2) = 123456800 </example>
/// <returns>Rounded number</returns>
public static int Round(this int number, int decimals)
{
if (decimals >= 0)
{
return number;
}
return (int) Round((decimal) number, decimals);
}
{
return decimals >= 0
? number
: (int) Round((decimal) number, decimals);
}
/// <summary>
/// Round to the number closest to 10^(-decimals). Supports negative decimals to round within the integer part.
/// Round to the number closest to 10^(-decimals). Negative decimals to round within the integer part.
/// </summary>
/// <param name="number">Number to be rounded</param>
/// <param name="decimals">Number of decimals to round to. Negative to round within the integer part, e.g. -3 will wound to the closes 1000.</param>
@ -819,16 +876,13 @@ namespace MathNet.Numerics
[CLSCompliant(false)]
public static uint Round(this uint number, int decimals)
{
if (decimals >= 0)
{
return number;
}
return (uint) Round((decimal) number, decimals);
return decimals >= 0
? number
: (uint) Round((decimal) number, decimals);
}
/// <summary>
/// Round to the number closest to 10^(-decimals). Supports negative decimals to round within the integer part.
/// Round to the number closest to 10^(-decimals). Negative decimals to round within the integer part.
/// </summary>
/// <param name="number">Number to be rounded</param>
/// <param name="decimals">Number of decimals to round to. Negative to round within the integer part, e.g. -3 will wound to the closes 1000.</param>
@ -836,16 +890,13 @@ namespace MathNet.Numerics
/// <returns>Rounded number</returns>
public static long Round(this long number, int decimals)
{
if (decimals >= 0)
{
return number;
}
return (long) Round((decimal) number, decimals);
return decimals >= 0
? number
: (long) Round((decimal) number, decimals);
}
/// <summary>
/// Round to the number closest to 10^(-decimals). Supports negative decimals to round within the integer part.
/// Round to the number closest to 10^(-decimals). Negative decimals to round within the integer part.
/// </summary>
/// <param name="number">Number to be rounded</param>
/// <param name="decimals">Number of decimals to round to. Negative to round within the integer part, e.g. -3 will wound to the closes 1000.</param>
@ -854,15 +905,13 @@ namespace MathNet.Numerics
[CLSCompliant(false)]
public static ulong Round(this ulong number, int decimals)
{
if (decimals >= 0)
{
return number;
}
return (ulong) Round((decimal) number, decimals);
return decimals >= 0
? number
: (ulong) Round((decimal) number, decimals);
}
/// <summary>
/// Round to the number closest to 10^(-decimals). Supports negative decimals to round within the integer part.
/// Round to the number closest to 10^(-decimals). Negative decimals to round within the integer part.
/// </summary>
/// <param name="number">Number to be rounded</param>
/// <param name="decimals">Number of decimals to round to. Negative to round within the integer part, e.g. -3 will wound to the closes 1000.</param>
@ -870,16 +919,13 @@ namespace MathNet.Numerics
/// <returns>Rounded number</returns>
public static short Round(this short number, int decimals)
{
if (decimals >= 0)
{
return number;
}
return (short) Round((decimal) number, decimals);
return decimals >= 0
? number
: (short) Round((decimal) number, decimals);
}
/// <summary>
/// Round to the number closest to 10^(-decimals). Supports negative decimals to round within the integer part.
/// Round to the number closest to 10^(-decimals). Negative decimals to round within the integer part.
/// </summary>
/// <param name="number">Number to be rounded</param>
/// <param name="decimals">Number of decimals to round to. Negative to round within the integer part, e.g. -3 will wound to the closes 1000.</param>
@ -888,16 +934,13 @@ namespace MathNet.Numerics
[CLSCompliant(false)]
public static ushort Round(this ushort number, int decimals)
{
if (decimals >= 0)
{
return number;
}
return (ushort) Round((decimal) number, decimals);
return decimals >= 0
? number
: (ushort) Round((decimal) number, decimals);
}
/// <summary>
/// Round to the number closest to 10^(-decimals). Supports negative decimals to round within the integer part.
/// Round to the number closest to 10^(-decimals). Negative decimals to round within the integer part.
/// </summary>
/// <param name="number">Number to be rounded</param>
/// <param name="decimals">Number of decimals to round to. Negative to round within the integer part, e.g. -3 will wound to the closes 1000.</param>
@ -921,21 +964,6 @@ namespace MathNet.Numerics
return divided * BigInteger.Pow(10, -decimals);
}
/// <summary>
/// Calculates the actual positive double precision machine epsilon - the smallest number that can be added to 1, yielding a results different than 1.
/// This is also known as unit roundoff error. According to the definition of Prof. Higham.
/// </summary>
/// <returns>Machine epsilon</returns>
static double MeasurePositiveMachineEpsilon()
{
double eps = 1.0d;
while ((1.0d + (eps / 2.0d)) > 1.0d)
eps /= 2.0d;
return eps;
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
static double Truncate(double value)
{

Loading…
Cancel
Save