Как увеличить самую маленькую дробную часть десятичного числа одной?

Я хочу увеличить самую маленькую дробную часть десятичного числа с той так, чтобы, например,

decimal d = 0.01
d++
d == 0.02

или

decimal d = 0.000012349
d++
d == 0.000012350

Как я делаю это?

9
задан Gustaf Carleson 10 February 2010 в 13:54
поделиться

4 ответа

Десятичный тип (.NET 2.0 и более поздние версии) сохраняет важные конечные нули, которые являются результатом вычисления или синтаксического анализа строки. Например. 1,2 * 0,5 = 0,60 (умножение двух чисел с точностью до одного десятичного знака дает результат с точностью до 2 десятичных знаков, даже если второй десятичный разряд равен нулю):

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

Ниже предполагается, что вы хотите учитывать все значащие цифры в десятичном значении. , т.е.

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

Однако, если вы хотите сначала удалить конечные нули, вы можете это сделать, например используя метод в этом ответе .

Для этого нет ничего встроенного. Вам придется сделать это самостоятельно, (а) определив, сколько цифр стоит после десятичной дроби, а затем (б) прибавив соответствующую сумму.

Чтобы определить, сколько цифр стоит после десятичной дроби, вы можете либо отформатировать как строку, а затем подсчитать их, либо, что более эффективно, вызвать decimal.GetBits (), результатом которого является массив из четырех целых чисел, содержащий коэффициент масштабирования в битах 16-23 четвертого целого числа.

Получив это, вы можете легко вычислить необходимое значение, которое нужно добавить к десятичному значению.

Вот реализация, использующая GetBits, которая «увеличивает» от нуля для отрицательных чисел. 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);
}

Или вот альтернатива (возможно, более элегантная):

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);
}
12
ответ дан 4 December 2019 в 15:17
поделиться

Я придумал новое решение, отличающееся от решения Джо, которое должно привести к небольшому повышению производительности.

public static decimal IncrementLowestDigit(this decimal value, int amount)
{
    int[] bits = decimal.GetBits(value);
    if (bits[0] < 0 && amount + bits[0] >= 0)
    {
        bits[1]++;
        if (bits[1] == 0)
        {
            bits[2]++;
        }
    }
    bits[0] += amount;
    return new decimal(bits);
}

Тест

Я протестировал свои результаты методами Джо.

private static void Test(int l, int m, int h, int e, int times)
{
    decimal a = new decimal(new[] { l, m, h, e });
    decimal b = a.IncrementLowestDigit(times);
    decimal c = IncrementLastDigit(a, times);

    Console.WriteLine(a);
    Console.WriteLine(b);
    Console.WriteLine(c);
    Console.WriteLine();
}

Test(0, 0, 0, 0x00000000, 1);
Test(0, 0, 0, 0x00000000, 2);
Test(0, 0, 0, 0x00010000, 1);
Test(0, 0, 0, 0x00010000, 2);
Test(0, 0, 0, 0x00020000, 1);
Test(0, 0, 0, 0x00020000, 2);

Test(-1, 0, 0, 0x00000000, 1);
Test(-1, 0, 0, 0x00000000, 2);
Test(-1, 0, 0, 0x00010000, 1);
Test(-1, 0, 0, 0x00010000, 2);
Test(-1, 0, 0, 0x00020000, 1);
Test(-1, 0, 0, 0x00020000, 2);

Test(-2, 0, 0, 0x00000000, 1);
Test(-2, 0, 0, 0x00000000, 2);
Test(-2, 0, 0, 0x00010000, 1);
Test(-2, 0, 0, 0x00010000, 2);
Test(-2, 0, 0, 0x00020000, 1);
Test(-2, 0, 0, 0x00020000, 2);
Test(-2, 0, 0, 0x00000000, 3);

Test(0, 1, 0, 0x00000000, 1);
Test(0, 1, 0, 0x00000000, 2);
Test(0, 1, 0, 0x00010000, 1);
Test(0, 1, 0, 0x00010000, 2);
Test(0, 1, 0, 0x00020000, 1);
Test(0, 1, 0, 0x00020000, 2);

Test(-1, 2, 0, 0x00000000, 1);
Test(-1, 2, 0, 0x00000000, 2);
Test(-1, 2, 0, 0x00010000, 1);
Test(-1, 2, 0, 0x00010000, 2);
Test(-1, 2, 0, 0x00020000, 1);
Test(-1, 2, 0, 0x00020000, 2);

Test(-2, 3, 0, 0x00000000, 1);
Test(-2, 3, 0, 0x00000000, 2);
Test(-2, 3, 0, 0x00010000, 1);
Test(-2, 3, 0, 0x00010000, 2);
Test(-2, 3, 0, 0x00020000, 1);
Test(-2, 3, 0, 0x00020000, 2);

Только для смеха

Я сделал тест производительности с 10 миллионами итераций на 3 ГГц. Чип Intel:

Мой: 11,6 нс

Джо: 32.1 ns

1
ответ дан 4 December 2019 в 15:17
поделиться

Это поможет:

decimal d = 0.01M;
int incr = 1;

int pos = d.ToString().IndexOf('.');
int len = d.ToString().Length - pos - 1;

if (pos > 0)
{
   double val = Convert.ToDouble(d);
   val = Math.Round(val * Math.Pow(10, len) + incr) / Math.Pow(10, len);
   d = Convert.ToDecimal(val);
}
else
   d += incr;
return d;
0
ответ дан 4 December 2019 в 15:17
поделиться

Что насчет этого:

static class DecimalExt {
    public static decimal PlusPlus(this decimal value) {
        decimal test = 1M;
        while (0 != value % test){
            test /= 10;
        }
        return value + test;
    }
}


class Program {

    public static void Main(params string[] args) {
        decimal x = 3.14M;
        x = x.PlusPlus(); // now is 3.15
    }
}

Я использовал здесь метод расширения; вы не можете переопределить оператор ++ для десятичного типа.

0
ответ дан 4 December 2019 в 15:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: