小编典典

我如何确保整数除法总是四舍五入?

c#

我想确保在必要时总是对整数除法进行四舍五入。有没有比这更好的方法了?正在进行很多强制转换。:-)

(int)Math.Ceiling((double)myInt1 / myInt2)

阅读 1030

收藏
2020-05-19

共1个答案

小编典典

更新:这个问题是我2013年1月博客的主题。感谢您提出的好问题!


要使整数算术正确,是很难的。到目前为止,已经充分地证明了这一点,当您尝试执行“聪明”的窍门时,很可能您犯了一个错误。而且,当发现缺陷时,更改代码以修正缺陷
而不考虑修正是否破坏了其他 问题并不是一种好的解决问题的技术。到目前为止,我已经发布了五个完全不特别困难的问题的五个不同的不正确的整数算术解决方案。

解决整数算术问题的正确方法(即增加首次获得正确答案的可能性的方法)是认真解决问题,一次解决一个问题,并使用良好的工程原理来做所以。

首先阅读要替换的规范。 整数除法规范明确指出:

  1. 除法将结果舍入为零

  2. 当两个操作数具有相同的符号时结果为零或正,而当两个操作数具有相反的符号时结果为零或负。

  3. 如果左操作数是可表示的最小int,而右操作数是–1,则发生溢出。[…]它是由实现定义的,涉及是否抛出[ArithmeticException]或未报告溢出,其结果值为左操作数。

  4. 如果右操作数的值为零,则抛出System.DivideByZeroException。

我们想要的是一个整数除法函数,该函数可计算商,但将结果 始终向上舍入 ,而不 总是向零舍入

因此,为该功能编写规范。 我们的函数int DivRoundUp(int dividend, int divisor)必须为每个可能的输入定义行为。这种不确定的行为令人深感忧虑,所以让我们消除它。我们将说我们的操作具有以下规范:

  1. 如果除数为零,则操作抛出

  2. 如果被除数为int.minval并且除数为-1,则操作抛出

  3. 如果没有余数-除为’偶’-则返回值为整数商

  4. 否则,它将返回 大于 商的 最小 整数,即,它总是四舍五入。 __

现在我们有了一个规范,因此我们知道我们可以提出一个 可测试的设计
。假设我们添加了一个附加的设计准则,该问题仅用整数算术即可解决,而不是将商计算为双精度,因为在问题陈述中明确拒绝了“双精度”解决方案。

那么我们必须计算什么呢?显然,要满足我们的规范,同时仅保留整数算术,我们需要知道三个事实。首先,整数商是多少?第二,除法是否没有余数?第三,如果不是,则是通过四舍五入来计算整数商吗?

现在我们有了规范和设计,我们可以开始编写代码了。

public static int DivRoundUp(int dividend, int divisor)
{
  if (divisor == 0 ) throw ...
  if (divisor == -1 && dividend == Int32.MinValue) throw ...
  int roundedTowardsZeroQuotient = dividend / divisor;
  bool dividedEvenly = (dividend % divisor) == 0;
  if (dividedEvenly) 
    return roundedTowardsZeroQuotient;

  // At this point we know that divisor was not zero 
  // (because we would have thrown) and we know that 
  // dividend was not zero (because there would have been no remainder)
  // Therefore both are non-zero.  Either they are of the same sign, 
  // or opposite signs. If they're of opposite sign then we rounded 
  // UP towards zero so we're done. If they're of the same sign then 
  // we rounded DOWN towards zero, so we need to add one.

  bool wasRoundedDown = ((divisor > 0) == (dividend > 0));
  if (wasRoundedDown) 
    return roundedTowardsZeroQuotient + 1;
  else
    return roundedTowardsZeroQuotient;
}

这很聪明吗?不,美丽吗?不,简短吗?否。根据规格正确吗? 我相信,但是我还没有完全测试它。 虽然看起来不错。

我们是这里的专业人士;使用良好的工程实践。研究您的工具,指定所需的行为,首先考虑错误情况,然后 编写代码以强调其明显的正确性。
并且,当您发现错误时,请先考虑您的算法是否存在严重缺陷,然后再随机开始交换比较方向并破坏已经起作用的内容。

2020-05-19