尝试计算框的分数时的十进制精度损失

时间:2012-03-02 22:49:10

标签: c# algorithm decimal rounding

我有一个场景,我有一个包含3个罐子的标准盒子。出于显示和查询的目的,我必须按其标准配置的十进制数量进行报告。不可能说1盒3罐,1盒2罐......等

例如,最初我将有 1盒3罐
然后我删除1罐,导致 0.66重复的3罐罐
然后我再删除1个罐子,导致 0.33重复的3罐罐子
然后我删除了最后的罐头,导致 0.000000000000000000000000000001 3罐装

当我删除最后一个时,我希望该值为 0盒3罐,因为现在已经从原始盒中删除了每个罐。我很欣赏由于在处理有限数量的位时无法表示0.33重复这一事实而导致精度损失。

问题对于那些在需要使用舍入系统(可能是财务方面)方面有更多经验的人来说,我有哪些方法可以解决这个问题?如何删除最后一个可能意味着该框不再存在?

修改
到底。我使用了Loren Pechtel的建议并存储了罐头的数量,然后当我需要显示多少标准盒子时我将罐头总数除以标准盒子中的罐头数量仍然给出递归结果但这对报告来说很好事情的一面。

以下是一些代码,我希望这些代码可以帮助更多地概述问题: -

     static void Main(string[] args)
     {
        var box = new StandardBox(3);
        var boxDetail = new BoxDetail(1.0m, box);

        var allBoxes = new AllBoxes();
        allBoxes.AddBox(boxDetail);
        allBoxes.RemoveItemFromBox(boxDetail, 1.0m);
        Console.WriteLine(allBoxes);
        allBoxes.RemoveItemFromBox(boxDetail, 1.0m);
        Console.WriteLine(allBoxes);
        allBoxes.RemoveItemFromBox(boxDetail, 1.0m);
        Console.WriteLine(allBoxes);
        Console.ReadLine();
    }
}

public class StandardBox
{
    private decimal _quantity;
    public StandardBox(decimal quantity){_quantity = quantity;}
    public decimal Quantity {get { return _quantity; }}
}

public class BoxDetail
{
    private decimal _quantity;
    private StandardBox _box;

    public BoxDetail(decimal quantity, StandardBox box)
    {
        _quantity = quantity;
        _box = box;
    }

    public void RemoveItem(decimal quantity)
    {
        var newQuantity = quantity / _box.Quantity;
        _quantity = _quantity - newQuantity;
    }

    public override string ToString()
    {
        return _quantity.ToString() + " of " + _box.Quantity.ToString();
    }
}

public class AllBoxes
{
    private List<BoxDetail> allBoxes = new List<BoxDetail>();

    public AllBoxes(){}

    public void AddBox(BoxDetail box){allBoxes.Add(box);}

    public void RemoveItemFromBox(BoxDetail box, decimal quantity)
    {
        var boxDetailLineToModify = allBoxes.Find(b => b.Equals(box));
        boxDetailLineToModify.RemoveItem(quantity);
    }

    public override string ToString()
    {
        var results = string.Empty;
        foreach (var box in allBoxes)
        {
            results += box.ToString() + "\n";
        }
        return results;
    } 
}

3 个答案:

答案 0 :(得分:7)

我解决这些问题的方法是不要这样做。

当它是财务资料时尤其如此。

财务方面通常很容易处理 - 用便士做所有工作,而不是美元。

我对你的问题的第一反应是储存罐而不是罐子。

答案 1 :(得分:2)

为什么不将int用于您的数量?似乎你不想让1.078261563(例如)罐头去掉 - 你呢?

在阅读了一下之后,我想我在这里看到了两难境地。看起来你正在将尺寸(盒子的最大容量),数量(盒子中当前包含的内容)组合成仅百分比(数量与大小的比率)。如果您稍微重新设计,跟踪尺寸和数量以及计算百分比已满,我认为它会清除:

class Box {
    public decimal Size { get; private set; }
    public decimal Quantity { get; private set; }
    public decimal PercentFull { get { return this.Quantity / this.Size; } }
    public Box(decimal size) {
        this.Size = size;
    }
    public void Remove(decimal quantity) 
    {
        this.Quantity -= quantity;
    }
    public void Add(decimal quantity) 
    {
        this.Quantity += quantity;
    }
    public override string ToString() {
        return this.PercentFull.ToString() + "% of " + this.Size.ToString();
            // Or, to be exact
            // return this.Quantity.ToString() + " of " + this.Size.ToString();
    }
}

根据您(可能已经消毒过的)示例,SizeQuantity更适合int,但似乎仍然可以使用相同的方法。< / p>

答案 2 :(得分:1)

如果您只是想显示 0,那么在显示合理的时候设置精度,比如2位小数:

public override string ToString()
{
    return _quantity.ToString("D2") + " of " + _box.Quantity.ToString();
}

如果您希望值与0(或任何其他值)进行比较,请将差异与某个小值进行比较:

public bool AreEqual(double value1, double value2)
{
    double EPSILON = 0.000001;
    return Math.Abs(value1 - value2) < EPSILON;
}

bool IsZero = AreEqual(_quantity,0);