Как округлить до ближайшего четного целого?

Моя последняя цель всегда округляется до ближайшего четного целого.

Например, номер 1122.5196 я хочу как результат 1122. Я пробовал следующие варианты:

Math.Round(1122.5196d, 0, MidpointRounding.ToEven);       // result 1123
Math.Round(1122.5196d, 0, MidpointRounding.AwayFromZero); // result 1123

В конце концов, то, что я хотел бы получить, всегда является ближайшим даже интуитивным. Например:

  • 1122.51 --> 1122
  • 1122.9 --> 1122 (поскольку ближайший int равен 1123 но он нечетный, а 1122 - ближе, чем 1124)
  • 1123.0 --> 1124 (следующее четное значение, следующее более высокое значение)

Я работаю только с положительными цифрами.

И так далее.

Есть какой-то метод, который делает это или я должен реализовать свой собственный метод?

Ответ 1

Попробуйте это (позвольте использовать Math.Round с MidpointRounding.AwayFromZero, чтобы получить "следующее четное значение", но масштабированное - 2 фактора):

double source = 1123.0;

// 1124.0
double result = Math.Round(source / 2, MidpointRounding.AwayFromZero) * 2;

Демо-версия:

double[] tests = new double[] {
     1.0,
  1123.1,
  1123.0,
  1122.9,
  1122.1,
  1122.0,
  1121.5,
  1121.0,
};

string report = string.Join(Environment.NewLine, tests
  .Select(item => $"{item,6:F1} -> {Math.Round(item / 2, MidpointRounding.AwayFromZero) * 2}"));

Console.Write(report);

Результат:

   1.0 -> 2     // In case of tie, next even value
1123.1 -> 1124
1123.0 -> 1124  // In case of tie, next even value
1122.9 -> 1122
1122.1 -> 1122
1122.0 -> 1122
1121.5 -> 1122
1121.0 -> 1122  // In case of tie, next even value

Ответ 2

Один лайнер:

double RoundToNearestEven(double value) =>
    Math.Truncate(value) + Math.Truncate(value) % 2;

скрипка

Объяснение: если у нас есть четное число с некоторыми цифрами после с плавающей запятой, нам нужно просто избавиться от этих цифр. Если у нас есть нечетное число, нам нужно сделать то же самое, а затем перейти к следующему целому числу, которое гарантировано будет четным.

PS Благодаря @DmitryBychenko за то, что бросание двойного в длинный - не самая яркая идея.

Ответ 3

Причина, по которой вы получаете результат 1123, даже при использовании

Math.Round(1122.5196d, 0, MidpointRounding.ToEven);

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

то есть. 1122.51 округляется до равно 1123.0 (обратите внимание, что, поскольку оно является десятичным, оно всегда будет хранить десятичное место, и поэтому.0 здесь делает это четным числом).

Вместо этого я бы написал функцию для этого, например:

   private int round_up_to_even(double number_to_round)
    {
        int converted_to_int = Convert.ToInt32(number_to_round);
        if (converted_to_int %2 == 0) { return converted_to_int; }
        double difference = (converted_to_int + 1) - number_to_round;
        if (difference <= 0.5) { return converted_to_int + 1; }
        return converted_to_int - 1;
    }

Ответ 4

Вот пример, который я нашел в msdn, который будет производить только ближайшие числа, кажется, подходит вашему делу хорошо,

using System;
class Example
{
public static void Main()
{
  // Define a set of Decimal values.
  decimal[] values = { 1.45m, 1.55m, 123.456789m, 123.456789m, 
                       123.456789m, -123.456m, 
                       new Decimal(1230000000, 0, 0, true, 7 ),
                       new Decimal(1230000000, 0, 0, true, 7 ), 
                       -9999999999.9999999999m, 
                       -9999999999.9999999999m };
  // Define a set of integers to for decimals argument.
  int[] decimals = { 1, 1, 4, 6, 8, 0, 3, 11, 9, 10};

  Console.WriteLine("{0,26}{1,8}{2,26}", 
                    "Argument", "Digits", "Result" );
  Console.WriteLine("{0,26}{1,8}{2,26}", 
                    "--------", "------", "------" );
  for (int ctr = 0; ctr < values.Length; ctr++)
    Console.WriteLine("{0,26}{1,8}{2,26}", 
                      values[ctr], decimals[ctr], 
                      Decimal.Round(values[ctr], decimals[ctr]));
  }
}

// The example displays the following output:
//                   Argument  Digits                    Result
//                   --------  ------                    ------
//                       1.45       1                       1.4
//                       1.55       1                       1.6
//                 123.456789       4                  123.4568
//                 123.456789       6                123.456789
//                 123.456789       8                123.456789
//                   -123.456       0                      -123
//               -123.0000000       3                  -123.000
 //               -123.0000000      11              -123.0000000
//     -9999999999.9999999999       9    -10000000000.000000000
 //     -9999999999.9999999999      10    -9999999999.9999999999

"При округлении средних значений алгоритм округления выполняет тест равенства. Из-за проблем с двоичным представлением и точностью в формате с плавающей запятой значение, возвращаемое методом, может быть неожиданным".