C #: Как мне сделать простую математику с округлением на целые числа? - PullRequest
19 голосов
/ 18 ноября 2008

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

137 * (3/4) = 103

Рассмотрим следующий неправильный код.

int width1 = 4;
int height1 = 3;

int width2 = 137;
int height2 = width2 * (height1 / width1);

Как правильно выполнять "целочисленную" математику в C #?

Мне действительно нужно сделать:

int height2 = (int)Math.Round(
      (float)width2 * ((float)height1 / (float)width1)
   );

Ответы [ 12 ]

16 голосов
/ 18 ноября 2008

Как сказано выше, вы должны сделать умножение перед делением. В любом случае вы могли бы написать свое выражение с приведением только делителя:

int height2 = (int)Math.Round(width2 * (height1 / (float)width1));
3 голосов
/ 18 ноября 2008

Сначала делайте умножение (если они вряд ли переполнятся), а затем деление.

3/4 ​​== 0 в целочисленной математике (целочисленная математика не округляется при делении, она усекается).

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

3 голосов
/ 18 ноября 2008
int height2 = (width2 * height1) / width1;
2 голосов
/ 16 марта 2011

Я думаю, что это самый элегантный способ сделать это:

Math.round(myinteger * 0.75);

Использование 0,75 вместо 3/4 приведет к неявному приведению к double / float, и почему бы не использовать функции по умолчанию, которые предусмотрены для этого?

1 голос
/ 06 июля 2014

Шесть лет (округлено) спустя, вот мой вклад - маленький трюк, который я узнал давно, и удивлен, что никто больше не упомянул здесь.

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

    int height2 = (width2 * height1 + width1 / 2) / width1;

На самом деле я бы не рекомендовал делать это в случаях, таких как ОП, где делитель является переменной. Вместо этого может быть лучше использовать Math.Round (), так как это гораздо легче понять.

Но в случаях, когда делитель является константой, я использую этот трюк. Так что вместо

    int height2 = width2 * height1 / 4;  // No rounding

Я бы использовал

    int height2 = (width2 * height1 + 2) / 4;

Вот более типичный пример

  private static Size ResizeWithPercentage(Size oldSize, int resizePercentage)
  {
     return new Size((oldSize.Width * resizePercentage + 50) / 100, 
                     (oldSize.Height * resizePercentage + 50) / 100);
  }

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

    int height2 = (width2 * height1 * 2 + width1) / (width1 * 2);

Это дает лучшие ответы в тех случаях, когда делителем является или может быть нечетное число.

1 голос
/ 06 сентября 2013

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

int height2 = (int)Math.Round(width2 * (height1 / (float)width1),MidpointRounding.AwayFromZero);

Чтобы увидеть разницу, запустите этот код в консоли

    Console.WriteLine((int)Math.Round(0.5));
    Console.WriteLine((int)Math.Round(1.5));
    Console.WriteLine((int)Math.Round(2.5));
    Console.WriteLine((int)Math.Round(3.5));
    Console.WriteLine((int)Math.Round(0.5, MidpointRounding.AwayFromZero));
    Console.WriteLine((int)Math.Round(1.5, MidpointRounding.AwayFromZero));
    Console.WriteLine((int)Math.Round(2.5, MidpointRounding.AwayFromZero));
    Console.WriteLine((int)Math.Round(3.5, MidpointRounding.AwayFromZero));
    Console.ReadLine();

Подробнее вы можете посмотреть в этой статье.

1 голос
/ 24 января 2009

Никогда не приводим к плавающей точке, он имеет даже меньшую точность, чем 32-разрядное целое число. Если вы собираетесь использовать плавающую точку, всегда используйте double вместо float.

1 голос
/ 19 ноября 2008

Я исправлю неправильный код, добавив ноль строк кода:

float width1 = 4;
float height1 = 3;

float width2 = 137;
float height2 = width2 * (height1 / width1);

Вы должны использовать float для переменных, которые могут содержать десятичные дроби. Это включает в себя высоты, рассчитанные из соотношений. Вы всегда можете привести к int позже, если это проблема.

0 голосов
/ 02 ноября 2013

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

Если последнее деление будет на нечетное число, умножьте числитель и знаменатель на 2, а затем действуйте, как указано выше. Например, чтобы вычислить округленное * 5/7, вычислите (a*10+1)>>1. Единственное, на что следует обратить внимание, это то, что вам может потребоваться расширить значения до большего числового типа, чтобы избежать переполнения или, если это невозможно, разделить деление на части. Например, чтобы вычислить * 14/15, вы можете вычислить ((a * 4/3 * 7) / 5 + 1) / 2. Это вычисление может все еще переполниться, если a слишком велико, но допустимый диапазон будет в три раза больше, чем если бы он оценивался без деления на 3 до другого деления. Обратите внимание, что подразделение операции сделает округление немного менее точным, но все же достаточно близким для многих целей.

0 голосов
/ 23 августа 2011

Конверсии всегда округляются так:

int height2 = Convert.ToInt32(width2 * height1 / (double)width1);
...