问题描述
我要确保,如果有必要整数的除法总是四舍五入。难道还有比这更好的办法吗?有很多铸件的事情。 : - )
(INT)Math.Ceiling((双)myInt1 / myInt2)
更新:这个问题问得的的主题。感谢伟大的问题!
获取整数运算正确的是很难的。由于充分迄今已经证明,你尝试做一个聪明的绝招的那一刻,赔率是好的,你犯了一个错误。当一个漏洞被发现,改变了code来修复这个安全漏洞的无需考虑是否修复打破别的东西的是不是一个很好的解决问题的方法。到目前为止,我们已经有,我认为五个不同的不正确的整数运算解决方案,发表在这个完全没有,尤其-困难的问题。
接近整数运算问题的正确方法 - 即,增加得到了答案在第一时间的可能性的方式 - 就是要认真解决这个问题,解决它在一步一个脚印,用良好的工程原则这样做。
通过阅读规范你想替换一下启动作为整数除法的规范中明确规定:
-
该部门将向零舍入结果
-
的结果是零或正的时,两个操作数具有相同的符号和零或负时,两个操作数具有相反的符号
-
如果左操作数是最小的重presentable int和右操作数为-1,则发生溢出。 [...]这是实现定义,是否[的ArithmeticException]被抛出或溢出去与所得到的值是它的左边操作数的报道。
-
如果右操作数的值为零,则引发System.DivideByZeroException。
我们要的是一个整数除法功能,它计算的商轮,但结果的总是向上的,不是的总是趋向于零的。
所以写了这个函数的规范。我们的功能 INT DivRoundUp(INT分红,INT除数)必须有行为所有可能的输入定义
。未定义行为深感忧虑,所以让我们消除它。我们会说,我们的操作有此说明:
-
如果除数为零操作抛出
-
如果股息int.minval和除数为-1操作抛出
-
如果没有余 - 分工是偶 - 则返回值是积分商
-
否则,它返回的最小的整数,它的大于的比商,也就是说,它始终向上舍入。
现在我们有一个规范,所以我们知道我们可以拿出一个可测性设计。假设我们增加一个额外的设计标准,该问题仅与整数运算来解决,而不是计算商作为一个双重的,因为双节解决方案中的问题陈述被明确拒绝。
那么,我们必须计算?显然,以满足我们的规范,而在整数运算只剩余,我们需要知道三个事实。首先,什么是整数商?其次,是无划分余数?第三,如果没有,是由四舍五入或上下计算整数商?
现在,我们有一个规范和设计,我们可以开始编写code。
公共静态INT DivRoundUp(INT被除数,除数INT)
{
如果(除数== 0)扔...
如果(除数== -1放大器;&安培;红利== Int32.MinValue)扔...
INT roundedTowardsZeroQuotient =股息/除数;
布尔dividedEvenly =(分红除数%)== 0;
如果(dividedEvenly)
返回roundedTowardsZeroQuotient; //在这一点上,我们知道,除数是不是零
//(因为我们抛出),我们知道,
//分红是不是零(因为本来没有余数)
//因此两者都是非零。他们要么是相同的符号,
//或相反的迹象。如果他们是相反的符号,那么我们绕过
//向上趋向于零,所以我们就大功告成了。如果他们是相同的符号,那么
//我们DOWN向零舍入,所以我们需要添加一个。 布尔wasRoundedDown =((除数大于0)==(股息大于0));
如果(wasRoundedDown)
返回roundedTowardsZeroQuotient + 1;
其他
返回roundedTowardsZeroQuotient;
}
这是聪明?美丽的号?第短?号按照规范正确吗?的我相信是这样,但我还没有完全测试它。的它看起来pretty好的,但。
在这里,我们是专业人士;用良好的工程实践。研究你的工具,指定所需的行为,首先考虑误差的情况下,和写code,强调其明显的正确性。当你发现一个bug,考虑你的算法是有严重缺陷的开始与之前你只是随机左右开始交换比较的方向,打破东西,已经工作。
I want to ensure that a division of integers is always rounded up if necessary. Is there a better way than this? There is a lot of casting going on. :-)
(int)Math.Ceiling((double)myInt1 / myInt2)
UPDATE: This question was the subject of my blog in January 2013. Thanks for the great question!
Getting integer arithmetic correct is hard. As has been demonstrated amply thus far, the moment you try to do a "clever" trick, odds are good that you've made a mistake. And when a flaw is found, changing the code to fix the flaw without considering whether the fix breaks something else is not a good problem-solving technique. So far we've had I think five different incorrect integer arithmetic solutions to this completely not-particularly-difficult problem posted.
The right way to approach integer arithmetic problems -- that is, the way that increases the likelihood of getting the answer right the first time - is to approach the problem carefully, solve it one step at a time, and use good engineering principles in doing so.
Start by reading the specification for what you're trying to replace. The specification for integer division clearly states:
The division rounds the result towards zero
The result is zero or positive when the two operands have the same sign and zero or negative when the two operands have opposite signs
If the left operand is the smallest representable int and the right operand is –1, an overflow occurs. [...] it is implementation-defined as to whether [an ArithmeticException] is thrown or the overflow goes unreported with the resulting value being that of the left operand.
If the value of the right operand is zero, a System.DivideByZeroException is thrown.
What we want is an integer division function which computes the quotient but rounds the result always upwards, not always towards zero.
So write a specification for that function. Our function int DivRoundUp(int dividend, int divisor)
must have behaviour defined for every possible input. That undefined behaviour is deeply worrying, so let's eliminate it. We'll say that our operation has this specification:
operation throws if divisor is zero
operation throws if dividend is int.minval and divisor is -1
if there is no remainder -- division is 'even' -- then the return value is the integral quotient
Otherwise it returns the smallest integer that is greater than the quotient, that is, it always rounds up.
Now we have a specification, so we know we can come up with a testable design. Suppose we add an additional design criterion that the problem be solved solely with integer arithmetic, rather than computing the quotient as a double, since the "double" solution has been explicitly rejected in the problem statement.
So what must we compute? Clearly, to meet our spec while remaining solely in integer arithmetic, we need to know three facts. First, what was the integer quotient? Second, was the division free of remainder? And third, if not, was the integer quotient computed by rounding up or down?
Now that we have a specification and a design, we can start writing code.
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;
}
Is this clever? No. Beautiful? No. Short? No. Correct according to the specification? I believe so, but I have not fully tested it. It looks pretty good though.
We're professionals here; use good engineering practices. Research your tools, specify the desired behaviour, consider error cases first, and write the code to emphasize its obvious correctness. And when you find a bug, consider whether your algorithm is deeply flawed to begin with before you just randomly start swapping the directions of comparisons around and break stuff that already works.
这篇关于我怎样才能确保整数的除法总是四舍五入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!