Rounding decimal (fast)

V

vooose

Consider a rounding up function:

public static decimal RoundUp(decimal val, decimal round)
{
return ((decimal)Math.Ceiling((double)(val/round)))*round;
}
Math.Ceiling (and Math.Floor for RoundDown) only take double,s so we
need to cast twice. Is there a better way?
 
J

Jon Skeet [C# MVP]

vooose said:
Consider a rounding up function:

public static decimal RoundUp(decimal val, decimal round)
{
return ((decimal)Math.Ceiling((double)(val/round)))*round;
}
Math.Ceiling (and Math.Floor for RoundDown) only take double,s so we
need to cast twice. Is there a better way?

Aside from Bruce's point - do you have any evidence that this is
actually the bottleneck in your application? It's always worth waiting
until you know you have a problem before trying micro-optimisation like
this. (In this particular case, Bruce's solution is much better - it's
much more readable as well as potentially faster.)
 
V

vooose

Thanks for your reply. According to the doc, Decimal.Round rounds to the
nearest. (see below)

I need one that roundsUp and roundsDown !

quote
Remarks
When d is exactly halfway between two rounded values, the result is the
rounded value that has an even digit in the far right decimal position.
For example, when rounded to two decimals, the value 2.345 becomes 2.34
and the value 2.355 becomes 2.36. This process is known as rounding
toward even, or rounding to nearest.
 
B

Bruce Wood

Rounds up:

decimal rounded = Decimal.Round(originalValue, 3);
if (rounded != originalValue)
{
rounded = Decimal.Round(originalValue + 0.0005, 3);
}

rounds down:

decimal rounded = Decimal.Round(originalValue, 3)
if (rounded != originalValue)
{
rounded = Decimal.Round(originalValue - 0.0005m, 3);
}

Not terribly pretty, but it will work. This sort of thing is easier
where the rounding system always takes the half (e.g. 0.5) _up_, not to
the nearest even digit. It's the latter that requires the first round
attempt and the test, unfortunately.

However, your original code looks as though you are trying to round to
the left of the decimal place, e.g. "to the next higher thousand" or
"to the next higher hundred." In this case, it gets easier:

decimal nextHigherThousand = ((int)(originalValue / 1000m)) * 1000m;
if (nextHigherThousand != originalValue)
{
nextHigherThousand += 1000m;
}

Since to cast to (int) truncates rather than rounding, the first
calculation will either give you the original value back (if it's
already a multiple of 1000) or round down. The test determines whether
the original value was a multiple of 1000 and, if it wasn't, adds 1000
to produce a "round up" effect. This will cost you one cast to int to
perform the truncation, and one (invisible) coercion from int back to
decimal.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top