繁体   English   中英

四舍五入小数并在C#中转换为%

[英]Round decimals and convert to % in C#

我有一个类似的概率列表

0.0442857142857143
0.664642857142857
0.291071428571429

我想将它们转换为最接近的百分比,以使百分比之和总计为100

所以像这样

0.0442857142857143 - 4 %
0.664642857142857  -  67 %
0.291071428571429  -  29 %

我不能依靠Math.Round始终给我提供总计为1的结果。什么是最好的方法?

这是可以完成工作的方法。

public int[] Round(params decimal[] values)
{
    decimal total = values.Sum();
    var percents = values.Select(x=> Math.Round(x/total*100)).ToArray();
    int totalPercent = perents.Sum();
    var diff = 100 - totalPercent;
    percents[percents.Lenght - 1] += diff;
    return percents;
}

有趣的答案集合。

这里的问题是,在四舍五入操作中出现累积错误。 在某些情况下,累积的误差会抵消掉-一些值向上取整,另一些值向下取整,从而抵消了总误差。 在其他情况下(例如此处的情况),舍入误差均为负,得出的累积总误差为(大约)-1。

通常情况下,解决此问题的唯一方法是跟踪总累积错误,并在该错误变得足够大时加/减。 这很繁琐,但是唯一正确的正确方法是:

static int[] ToIntPercents(double[] values)
{
    int[] results = new int[values.Length];
    double error = 0;
    for (int i = 0; i < values.Length; i++)
    {
        double val = values[i] * 100;
        int percent = (int)Math.Round(val + error);
        error += val - percent;
        if (Math.Abs(error) >= 0.5)
        {
            int sign = Math.Sign(error);
            percent += sign;
            error -= sign;
        }
        results[i] = percent;
    }

    return results;
}

对于任何大小约为+1.0000(或足够接近)的数组,此代码都会产生合理的结果。 数组可以包含负值和正值,只要总和足够接近+1.0000,就不会引起严重错误。

该代码会累积舍入误差,当总误差超出可接受的范围-0.5 < error < +0.5它将调整输出。 使用此方法,您的数字输出数组将为: [4, 67, 29] 您可以将可接受的错误范围更改为0 <= error < 1 ,给出输出[4, 66, 30] ,但是当数组包含负数时,这会导致奇怪的结果。 如果这是您的偏好, if方法中间的if语句更改为:

if (error < 0 || error >= 1)

您可以将数字乘以100(如果有十进制数字)

0.0442857142857143 * 100 = 4 %
0.664642857142857 * 100 = 66 %
0.291071428571429 * 100 = 29 %

E:正确,0.291071428571429的总和不会达到30%...

由于您似乎并不在乎哪个数字会增加,因此我将使用最后一个。 该算法非常简单,适用于必须添加1的.4边缘情况和必须删除1的.5边缘情况:

1)将每个数字除最后一个数字四舍五入2)从您的总和中减去100 3)将余数分配给最后一个数字

作为扩展方法,它看起来像这样:

public static int[] SplitIntoPercentage(this double[] input)
{
    int[] results = new int[input.Length];
    for (int i = 0; i < input.Length - 1; i++)
    {
        results[i] = (int)Math.Round(input[i] * 100, MidpointRounding.AwayFromZero);
    }

    results[input.Length - 1] = 100 - results.Sum();

    return results;
}

这是相关的单元测试:

    [TestMethod]
    public void IfSumIsUnder100ItShouldBeBumpedToIt()
    {
        double[] input = new []
            {
                0.044,
                0.664,
                0.294
            };

        var result = input.SplitIntoPercentage();

        Assert.AreEqual(100, result.Sum());
        Assert.AreEqual(4, result[0]);
        Assert.AreEqual(66, result[1]);
        Assert.AreEqual(30, result[2]);
    }

    [TestMethod]
    public void IfSumIsOver100ItShouldBeReducedToIt()
    {
        double[] input = new[]
            {
                0.045,
                0.665,
                0.295
            };

        var result = input.SplitIntoPercentage();

        Assert.AreEqual(100, result.Sum());
        Assert.AreEqual(5, result[0]);
        Assert.AreEqual(67, result[1]);
        Assert.AreEqual(28, result[2]);
    }

重构后,结果如下所示:

public static int[] SplitIntoPercentage(this double[] input)
{
    int[] results = RoundEachValueButTheLast(input);
    results = SetTheLastValueAsTheRemainder(input, results);

    return results;
}

private static int[] RoundEachValueButTheLast(double[] input)
{
    int[] results = new int[input.Length];
    for (int i = 0; i < input.Length - 1; i++)
    {
        results[i] = (int)Math.Round(input[i]*100, MidpointRounding.AwayFromZero);
    }

    return results;
}

private static int[] SetTheLastValueAsTheRemainder(double[] input, int[] results)
{
    results[input.Length - 1] = 100 - results.Sum();

    return results;
}

逻辑是,首先我们必须将“十进制后的数值”四舍五入,然后对整个值进行四舍五入。

  static long PercentageOut(double value)
    {
        value = value * 100;
        value = Math.Round(value, 1, MidpointRounding.AwayFromZero); // Rounds "up"
        value = Math.Round(value, 0, MidpointRounding.AwayFromZero); // Rounds to even

        return Convert.ToInt64(value);
    }


    static void Main(string[] args)
    {


        double d1 = 0.0442857142857143;
        double d2 = 0.664642857142857;
        double d3 = 0.291071428571429;

        long l1 = PercentageOut(d1);
        long l2 = PercentageOut(d2);
        long l3 = PercentageOut(d3);

        Console.WriteLine(l1);
        Console.WriteLine(l2);
        Console.WriteLine(l3);
     }

产量

   4
   67
   29
   ---
sum is 100 %

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM