In This Post We Remember You Can Do Math With Computers

Back to Listing

In This Post We Remember You Can Do Math With Computers

04 Jun, 2011

I recently ran into an interesting problem. I'll try to describe the problem with out going into specific details of the domain the problem lives in. Let's say I have a list of elements and each element have a property called weight that contains a decimal that represents that elements percentage in the entire list. If you sum the weights it should add up to 1M or 100%. This list of elements was loaded up from a text file and the text file weight had a precision of nine meaning that the number was represented with nine numbers after the decimal, 0.000000000. With this list I need to modify the precision to six and still have the sum of the weights add up to 100% exactly.

Here is what I came up with.

public class WeightRounder  
    private const int SIGNIFIGANT_DIGITS = 6;

    public IList<element> RoundOff(IList<element> elements)
        if (elements.Count > 0)
            RedistributeWeightError(elements, GetTotalWeightError(elements));
        return model;

    private static void MakeRoundedModel(IEnumerable<element> elements)
        model.Each(x => x.UpstreamWeight = Math.Round(x.Weight, SIGNIFIGANT_DIGITS));

    private void RedistributeWeightError(IEnumerable<element> elements, decimal totalWeightError)
        int errorSign = Math.Sign(totalWeightError);
        decimal step = (decimal) Math.Pow(10, -SIGNIFIGANT_DIGITS)*errorSign;

        elements.OrderByDescending(x => x.UpstreamWeight)
                .TakeWhile(x => Math.Abs(totalWeightError) > decimal.Zero)
                .Each(x =>
                          x.UpstreamWeight += step;
                          totalWeightError -= step;

        //indicates the elements were nowhere near 100% to begin with.
        if (totalWeightError != 0)
            throw new ApplicationException("Rounding failed. Total weight error {0} was to large to handle.".FormatWith(totalWeightError));

    private decimal GetTotalWeightError(IEnumerable<element> elements)
        var totalWeightError = decimal.One;
        elements.Each(x => totalWeightError -= x.UpstreamWeight);
        return totalWeightError;

Thoughts, comments or rants on my general approach and math skills appreciated.

Share this story

Bobby Johnson

About Author

I am a passionate engineer with an interest in shipping quality software, building strong collaborative teams and continuous improvement of my skills, team and the product.

comments powered by Disqus
Back to top