I have a scenario where I have a standard box that contains 3 cans. For display and query purposes I have to report in terms of a decimal amount of its standard configuration.It is not possible to say 1 box of 3 cans, 1 box of 2 cans...etc
For example, initially I will have 1 box of 3 cans
I then remove 1 can resulting in 0.66 recurring box of 3 cans
I then remove 1 more can resulting in 0.33 recurring box of 3 cans
I then remove the final can resulting in 0.0000000000000000000000000001 box of 3 cans
When I remove the final can I would like the value to be 0 boxes of 3 cans as every can has now been removed from the original box. I appreciate that there is a loss of precision due to the fact that it is not possible to represent 0.33 recurring when you are dealing with a finite number of bits.
Question: For people who have more experience in systems that need to use rounding (financial maybe) what are my options to deal with this problem? How would you go about making the removal of the last can mean that the box no longer exists?
Edit:
In the end. I used Loren Pechtel suggestion and stored the number of cans then when I need to show how many standard boxes I divide the total number of cans by the number of cans in a standard box which still gives a recursive result but this is fine for the reporting side of things.
Here is some code which I hope will help in outlining the problem some more:-
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;
}
}
My approach to such problems is don't do it.
That's especially true when it's financial stuff.
The financial stuff is generally quite easy to deal with--do all your work in pennies, not dollars.
My first reaction to your problem would be to store cans rather than boxes of cans.
If you just want to display 0, then set the precision when you are displaying to something reasonable, say 2 decimal places:
If you want to compare the value to 0 (or any other value), compare the difference against some small value:
Why not use
int
for your quantities? Doesn't seem like you want to allow the removal of 1.078261563 (for example) cans anyway - do you?After reading a bit more, I think I see the dilemma here. It seems like you're combining Size (the maximum capacity of the box), Quantity (what's currently contained in the box) into just Percent Full (the ratio of Quantity to Size). If you redesign slightly, tracking Size and Quantity and calculating Percent Full, I think it'll clear things up:
It still seems, based on your (possibly sanitized) example, that
Size
andQuantity
would be better suited forint
- but the math works out the same either way.