0

I'm trying to create a function that rounds a number to the nearest multiple of a given number as integer.

So if the number is 15 we have

  • 14,4 rounds to 15
  • -14,4 rounds to -15
  • 14,5 rounds to 15
  • 28 rounds to 30
  • -28 rounds to -30

and so on. I have already in place some code but seems to not work as expected:

public static int RoundToFactor(float number, float Factor)
{
        int returnNumber;

        if((number%Factor) == 0) {
            returnNumber = (int)number;
        }

        returnNumber = (int) (Mathf.Round(number/Factor)*Factor);

        return returnNumber;
}
Bernhard Barker
  • 54,589
  • 14
  • 104
  • 138
Pasquale Sada
  • 327
  • 2
  • 4
  • 12

2 Answers2

1

Here's an extension method that uses simple logic and System.Math methods (which may be more optimized).

using System;

public static class NumericRoundingExtensions
{
    /// <summary>
    /// Rounds a number to the nearest multiple of another number
    /// </summary>
    /// <param name="value">The value to round</param>
    /// <param name="factor">The factor to round to a multiple of. Must not be zero.</param>
    /// <param name="mode">Defines direction to round if <paramref name="value"/> is exactly halfway between two multiples of <paramref name="factor"/></param>
    /// <remarks>
    /// Use with caution when <paramref name="value"/> is large or <paramref name="factor"/> is small.
    /// </remarks>
    /// <exception cref="DivideByZeroException">If <paramref name="factor"/> is zero</exception>
    public static double RoundToNearestMultipleOfFactor(this double value, double factor, MidpointRounding mode = MidpointRounding.AwayFromZero)
    {
        return Math.Round(value / factor, mode) * factor;
    }
}

If you are using .Net Framework, then the mode can be AwayFromZero or ToEven. If you are using a newer version of .Net, there are six different modes for handling values that are halfway between two multiples of the factor. Midpoint Rounding documentation

I found this question searching for a method for long values that doesn't convert to floating point. If you happen to be working with integral types, like long or int, then it is best to use the following:

public static long RoundToNearestMultipleOfFactor(this long value, long factor)
{
    if (factor == 0)
    {
        throw new ArgumentOutOfRangeException(nameof(factor), factor, "Cannot be zero");
    }

    var halfAbsFactor = Math.Abs(factor) >> 1;
    return value + Math.Sign(value) * (halfAbsFactor - (Math.Abs(value) % factor + halfAbsFactor % factor) % factor);
}

For a full extension method class for integral types, see my answer to a similar question

Jon
  • 9,156
  • 9
  • 56
  • 73
0

Here is a method I made. It should suit your needs. I added a extra parameter asking to round up or down when it is equally close. (also if you notice that the numbers look like there wrong when they're negative for example 199 rounded to nearest factor of 2 rounded, up if necessary, is 200. change 199 to -199 and the outcome becomes -198, this is not a bug it is simply rounding up.)

public static double RoundToFactor(double Number, int Factor, bool RoundDirection = true)
    {/*round direction: in the event that the distance is 
      * equal from both next factors round up (true) down (false)*/

        double multiplyBy;
        if ((Number % Factor).Equals(0f))
        {
            return Number;
        }
        else
        {
            multiplyBy = Math.Round(Number / Factor);
            int Low = (int)multiplyBy - 1;
            int Mid = (int)multiplyBy;
            int High = (int)multiplyBy + 1;

            List<double> li = new List<double>() { Low, Mid, High };
            double minDelta = double.MaxValue;
            double Closest = 0d;

            foreach (double dbl in li)
            {
                double db = dbl * Factor;
                double Delta = (db < Number) ? (Number - db) : (db - Number);
                if (RoundDirection ? Delta <= minDelta : Delta < minDelta)
                {
                    minDelta = Delta;
                    Closest = db;
                }
            }
            return Closest;

        }

        throw new Exception("Math has broken!!!");
    }
Page
  • 63
  • 1
  • 6