简体   繁体   English

除法时的十进制舍入误差(C#)

[英]Decimal rounding errors upon division (C#)

I have basically four numbers (say 100, 200, 300, 400), and I need to calculate the probability as 100/(100+200+300+400), 200/(100+200+300+400), and so on. 我基本上有四个数字(比如100,200,300,400),我需要计算概率为100 /(100 + 200 + 300 + 400),200 /(100 + 200 + 300 + 400),等等上。

When I use the decimal data type to store these probabilities, they don't up to one due to round issues. 当我使用十进制数据类型来存储这些概率时,由于圆形问题,它们不会达到一个。 What's the best way to past this without making the probabilities too inaccurate? 在不使概率过于不准确的情况下,最好的方法是什么? Basically I do this calculation many many times, so I don't want to have to change all the divisions into Math.Round stuff. 基本上我做了很多次这个计算,所以我不想把所有的部分改成Math.Round的东西。 :| :|

The solution is straightforward: if it hurts when you do that then don't do that . 解决方案很简单: 如果你这样做会很痛,那就不要这样做了

If you have rational probabilities, that is, probabilities that are ratios of whole numbers, and you want them to add to exactly one, then don't convert them to decimal or double in the first place . 如果您有合理概率,即整数的比率概率,并且您希望它们只添加一个,那么首先不要将它们转换为十进制或双精度 Use an arbitrary-precision rational type to represent your arbitrary precision rationals. 使用任意精度的有理类型来表示任意精度的有理数。

There's an arbitrary-precision rational type included with Microsoft Solver Foundation; Microsoft Solver Foundation附带了一个任意精度的理性类型; you could download and use that. 你可以下载并使用它。 Or, it is easy to write your own by simply making an immutable struct that has two BigIntegers for the numerator and denominator, and then write implementations of the operators you need. 或者,通过简单地创建一个具有两个BigIntegers作为分子和分母的不可变结构,然后编写所需运算符的实现,很容易编写自己的结构。

There is no call to Math.Round that will resolve this the way you want. 没有调用Math.Round可以按照您想要的方式解决这个问题。 The issue is that all of the roundings must work together - ie. 问题是所有的舍入必须一起工作 - 即。 have awareness that individually they are all right but viewed together they are wrong. 我们已经意识到他们一切都是正确的,但他们一起看是错的。

One way to deal with rounding error of division, is to adjust the values to accomodate it: 处理除法舍入误差的一种方法是调整值以适应它:

List<decimal> decimals = new List<decimal>() { 100m, 200m, 300m, 300m };
decimal total = decimals.Sum();

List<decimal> probabilities = decimals.Select(x => x / total).ToList();
decimal sum = probabilities.Sum();
decimal error = 1.0m - sum;
Console.WriteLine("{0}, {1}", sum, error);

probabilities[0] += error; //put all of the error into the first item.
decimal newSum = probabilities.Sum();

Console.WriteLine(newSum);

More sophisticated approaches spread the error over the values. 更复杂的方法将错误分散在值上。 For example, spreading 0.0000000000000000000000000004 over 4 values instead of 1. 例如,在4个值而不是1上传播0.0000000000000000000000000004。

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

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