Как округлить до ближайшего четного числа? - PullRequest
0 голосов
/ 04 сентября 2018

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

Например, число 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 ( следующее четное значение , следующее большее четное значение)

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

и т. Д.

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

Ответы [ 4 ]

0 голосов
/ 07 сентября 2018

Вот пример функции, которую я нашел на 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

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

0 голосов
/ 04 сентября 2018

Причина, по которой вы получаете результат 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;
    }
0 голосов
/ 04 сентября 2018

Один лайнер:

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

Fiddle

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

P.S. Спасибо @DmitryBychenko за то, что он указал, что приведение двойного к длинному не самая лучшая идея.

0 голосов
/ 04 сентября 2018

Попробуйте это (давайте использовать 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
...