Guy
Guy

Reputation: 67310

How do I round a decimal to a specific fraction in C#?

In C# rounding a number is easy:

Math.Round(1.23456, 4); // returns 1.2346

However, I want to round a number such that the fractional part of the number rounds to the closest fractional part of a predefined fraction (e.g. 1/8th) and I'm trying to find out if the .NET library already has this built in.

So, for example, if I want to round a decimal number to a whole eighth then I'd want to call something like:

Math.RoundFractional(1.9, 8); // and have this yield 1.875
Math.RoundFractional(1.95, 8); // and have this yield 2.0

So the first param is the number that I want to round and the second param dictates the rounding fraction. So in this example, after rounding has taken place the figures following the decimal point can only be one of eight values: .000, .125, .250, .375, .500, .625, .750, .875

The Questions: Is this function built into .NET somewhere? If not, does anybody have a link to a resource that explains how to approach solving this problem?

Upvotes: 5

Views: 10581

Answers (3)

amorenojr
amorenojr

Reputation: 41

This is a late response, but for this is for anyone looking for a more comprehensive solution to convert a double as the original post describes. Below is a static class that provides extension methods for doubles to accomplish this in various ways.

using System;

/// <summary>
/// Author: Aniceto Moreno Jr
/// Email:  [email protected]
/// Date:   03/23/2020
/// </summary>
public static class DoubleExtensions
{
    /// <summary>
    /// Rounds the value to the nearest increment. 
    /// Assumes mid-point rounding, value >= 0.5 rounds up, value < 0.5 rounds down.
    /// </summary>
    /// <param name="Value"></param>
    /// <param name="increment">Enter the increment value to round toward.</param>
    /// <returns>Returns the value rounded to the nearest increment value.</returns>
    public static double RoundToNearest(this double Value, double increment)
    {
        // Returning the value rounded to the nearest increment value.
        return Math.Round(Value * Math.Pow(increment, -1), 0) * increment;
    }

    /// <summary>
    /// Rounds down the value to the nearest increment. 
    /// </summary>
    /// <param name="Value"></param>
    /// <param name="increment">Enter the increment value to round down toward.</param>
    /// <returns>Returns the value rounded down to the nearest increment value.</returns>
    public static double FloorToNearest(this double Value, double increment)
    {
        // Returning the value rounded down to the nearest increment value.
        return Math.Floor(Value * Math.Pow(increment, -1)) * increment;
    }

    /// <summary>
    /// Rounds up the value to the nearest increment. 
    /// </summary>
    /// <param name="Value"></param>
    /// <param name="increment">Enter the increment value to round up toward.</param>
    /// <returns>Returns the value rounded up to the nearest increment value.</returns>
    public static double CeilingToNearest(this double Value, double increment)
    {
        // Returning the value rounded up to the nearest increment value.
        return Math.Ceiling(Value * Math.Pow(increment, -1)) * increment;
    }

    /// <summary>
    /// Rounds the value down to the nearest imperial fractional increment
    /// and converts the value into an Inch-Fraction (IF) string. 
    /// Note: Assumes value is in inches and does not convert to Feet-Inch-Fraction (FIF)
    /// </summary>
    /// <param name="Value"></param>
    /// <param name="maxDenominator">Enter the maximum denominator to round toward (i.e. 1/16 --> 16)</param>
    /// <returns>Returns the value rounded down to the nearest increment value based on the maxDenominator.</returns>
    public static string FloorToInchFraction(this double Value, int maxDenominator)
    {
        // Returning the rounded value converted into an Inch-Fraction (IF) string.
        return Value.FloorToNearest(Math.Pow(maxDenominator, -1)).ToInchFraction(maxDenominator);
    }

    /// <summary>
    /// Rounds the value up to the nearest imperial fractional increment
    /// and converts the value into an Inch-Fraction (IF) string. 
    /// Note: Assumes value is in inches and does not convert to Feet-Inch-Fraction (FIF)
    /// </summary>
    /// <param name="Value"></param>
    /// <param name="maxDenominator">Enter the maximum denominator to round toward (i.e. 1/16 --> 16)</param>
    /// <returns>Returns the value rounded up to the nearest increment value based on the maxDenominator.</returns>
    public static string CeilingToInchFraction(this double Value, int maxDenominator)
    {
        // Returning the rounded value converted into a fraction string.
        return Value.CeilingToNearest(Math.Pow(maxDenominator, -1)).ToInchFraction(maxDenominator);
    }

    /// <summary>
    /// Rounds the value to the nearest increment value based on the maximum denominator specified.
    /// Assumes mid-point rounding, value >= 0.5 rounds up, value < 0.5 rounds down.
    /// </summary>
    /// <param name="Value"></param>
    /// <param name="maxDenominator">Enter the maximum denominator to round toward (i.e. 1/16 --> 16)</param>
    /// <returns>Returns the value rounded to the nearest increment value based on the maxDenominator.</returns>
    public static string ToInchFraction(this double Value, int maxDenominator)
    {
        // Calculating the nearest increment of the value
        // argument based on the denominator argument.
        double incValue = Value.RoundToNearest(Math.Pow(maxDenominator, -1));

        // Identifying the whole number of the argument value.
        int wholeValue = (int)Math.Truncate(incValue);

        // Calculating the remainder of the argument value and the whole value.
        double remainder = incValue - wholeValue;

        // Checking for the whole number case and returning if found.
        if (remainder == 0.0) { return wholeValue.ToString() + (char)34; }

        // Iterating through the exponents of base 2 values until the
        // maximum denominator value has been reached or until the modulus
        // of the divisor.
        for (int i = 1; i < (int)Math.Log(maxDenominator, 2) + 1; i++)
        {
            // Calculating the denominator of the current iteration
            double denominator = Math.Pow(2, i);

            // Calculating the divisor increment value
            double divisor = Math.Pow(denominator, -1);

            // Checking if the current denominator evenly divides the remainder
            if ((remainder % divisor) == 0.0) // If, yes
            {
                // Calculating the numerator of the remainder 
                // given the calculated denominator
                int numerator = Convert.ToInt32(remainder * denominator);

                // Returning the resulting string from the conversion.
                return (wholeValue > 0 ? wholeValue.ToString() + "-" : "") + numerator.ToString() + "/" + ((int)denominator).ToString() + (char)34;
            }
        }

        // Returns Error if something goes wrong.
        return "Error";
    }

    /// <summary>
    /// Rounds the value down to the nearest imperial fractional increment
    /// and converts the value into an Feet-Inch-Fraction (FIF) string. 
    /// </summary>
    /// <param name="Value"></param>
    /// <param name="maxDenominator">Enter the maximum denominator to round toward (i.e. 1/16 --> 16)</param>
    /// <returns>Returns the value rounded down to the nearest increment value based on the maxDenominator.</returns>
    public static string FloorToFeetInchFraction(this double Value, int maxDenominator)
    {
        // Returning the rounded value converted into an Feet-Inch-Fraction (FIF) string.
        return Value.FloorToNearest(Math.Pow(maxDenominator, -1)).ToFeetInchFraction(maxDenominator);
    }

    /// <summary>
    /// Rounds the value up to the nearest imperial fractional increment
    /// and converts the value into an Feet-Inch-Fraction (FIF) string. 
    /// </summary>
    /// <param name="Value"></param>
    /// <param name="maxDenominator">Enter the maximum denominator to round toward (i.e. 1/16 --> 16)</param>
    /// <returns>Returns the value rounded up to the nearest increment value based on the maxDenominator.</returns>
    public static string CeilingToFeetInchFraction(this double Value, int maxDenominator)
    {
        // Returning the rounded value converted into a fraction string.
        return Value.CeilingToNearest(Math.Pow(maxDenominator, -1)).ToFeetInchFraction(maxDenominator);
    }

    /// <summary>
    /// Rounds the value to the nearest increment value based on the maximum denominator specified.
    /// Assumes mid-point rounding, value >= 0.5 rounds up, value < 0.5 rounds down.
    /// </summary>
    /// <param name="Value"></param>
    /// <param name="maxDenominator">Enter the maximum denominator to round toward (i.e. 1/16 --> 16)</param>
    /// <returns>Returns the value rounded to the nearest increment value based on the maxDenominator.</returns>
    public static string ToFeetInchFraction(this double Value, int maxDenominator)
    {
        // Calculating the nearest increment of the value
        // argument based on the denominator argument.
        double incValue = Value.RoundToNearest(Math.Pow(maxDenominator, -1));

        // Calculating the remainder of the argument value and the whole value.
        double FeetInch = Math.Truncate(incValue) / 12.0;

        // Calculating the remainder of the argument value and the whole value.
        int Feet = (int)Math.Truncate(FeetInch);

        // Calculating remaining inches.
        incValue -= (double)(Feet * 12.0);

        // Returns the feet plus the remaining amount converted to inch fraction.
        return (Feet > 0 ? Feet.ToString() + (char)39 + " " : "") + incValue.ToInchFraction(maxDenominator);
    }
}

Examples of use and implementation:

double testValue0 = 1.0625; // 1―1/16"
Console.WriteLine(testValue0.ToInchFraction(32)); // 1―1/16"

double testValue1 = 1.515625; // 1―33/64"
Console.WriteLine(testValue1.CeilingToInchFraction(16)); // 1―9/16"

double testValue2 = 1.03125; // 1―1/32"
Console.WriteLine(testValue2.FloorToInchFraction(16)); // 1"

double testValue3 = 5 / 25.4; // 5 mm = 0.1969 in
Console.WriteLine(testValue3.ToInchFraction(32)); // 3/16"
Console.WriteLine(testValue3.ToInchFraction(64)); // 13/64"

Upvotes: 0

paxdiablo
paxdiablo

Reputation: 881683

Don't know if it's built into .NET but I would simply do:

Math.Round(x * 8, 0) / 8;

to round it to the nearest 8th.

Substitute your favorite number for other "resolutions".

Upvotes: 6

Greg Hewgill
Greg Hewgill

Reputation: 993461

You could do this:

Math.Round(n * 8) / 8.0

Upvotes: 21

Related Questions