Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to increase a decimal's smallest fractional part by one?

Tags:

c#

math

decimal

I want to increase a decimal's smallest fractional part with one so that for example

decimal d = 0.01
d++
d == 0.02

or

decimal d = 0.000012349
d++
d == 0.000012350

How do i do this?

like image 644
Gustaf Carleson Avatar asked Feb 10 '10 13:02

Gustaf Carleson


1 Answers

The decimal type (.NET 2.0 and later) retains significant trailing zeroes that are the result of a calculation or as a result of parsing a string. E.g. 1.2 * 0.5 = 0.60 (multiplying two numbers accurate to one decimal place gives a result accurate to 2 decimal places, even when the second decimal place is zero):

decimal result = 1.2M * 0.5M;
Console.WriteLine(result.ToString()); // outputs 0.60

The following assumes you want to consider all significant digits in your decimal value, i.e.

decimal d = 1.2349M;       // original  1.2349;
d = IncrementLastDigit(d); // result is 1.2350;
d = IncrementLastDigit(d); // result is 1.2351; (not 1.2360).

However if you want to first remove trailing zeroes, you can do so, e.g. using the technique in this answer.

There's nothing built-in to do this. You'll have to do it yourself by (a) determining how many digits there are after the decimal, then (b) adding the appropriate amount.

To determine how many digits there are after the decimal, you can either format as a string, then count them, or more efficiently, call decimal.GetBits(), the result of which is an array of four integers that contains the scaling factor in bits 16-23 of the fourth integer.

Once you have that you can easily calculate the required value to add to your decimal value.

Here's an implementation that uses GetBits, which "increments" away from zero for negative numbers IncrementLastDigit(-1.234M) => -1.235M.

static decimal IncrementLastDigit(decimal value)
{
    int[] bits1 = decimal.GetBits(value);
    int saved = bits1[3];
    bits1[3] = 0;   // Set scaling to 0, remove sign
    int[] bits2 = decimal.GetBits(new decimal(bits1) + 1);
    bits2[3] = saved; // Restore original scaling and sign
    return new decimal(bits2);
}

Or here's an alternative (perhaps slightly more elegant):

static decimal GetScaledOne(decimal value)
{
    int[] bits = decimal.GetBits(value);
    // Generate a value +1, scaled using the same scaling factor as the input value
    bits[0] = 1;
    bits[1] = 0;
    bits[2] = 0;
    bits[3] = bits[3] & 0x00FF0000;
    return new decimal(bits);
}

static decimal IncrementLastDigit(decimal value)
{
    return value < 0 ? value - GetScaledOne(value) : value + GetScaledOne(value);
}
like image 118
Joe Avatar answered Sep 27 '22 17:09

Joe