我正在使用在各种报告中使用的函数RptRound()
的C源代码,它使用两个值,一个金额和一个第二个值(如计数),将它们分开然后舍入结果。划分的金额通常是货币金额,如美元乘以100美分的金额。
我的问题是,如果lAmount
和sDivisor
的值均为负值,当前使用的函数是否会提供正确的舍入,从而导致其除法的正结果。
考虑到四舍五入的问题,它似乎并没有像我原先想象的那样切割和干燥。然而,这里的意图似乎是进行Round-Half-Up类型的舍入。或者更确切地说是圆形 - 半离开。但是,如果除数sDivisor
的值为正,则此函数仅执行此操作。
目前的功能如下:
DCURRENCY RptRound( DCURRENCY lAmount, SHORT sDivisor )
{
if (sDivisor) { /* Not Divide 0 Case */
D13DIGITS Quot, Quot1;
if ( lAmount < 0 ) {
Quot1 = -5;
} else {
Quot1 = 5;
}
Quot = lAmount * 10;
Quot /= (LONG)sDivisor;
Quot += Quot1;
Quot /= 10;
return Quot;
}
return (0);
}
看起来只要sDivisor
总是正数,那么这个函数将给出正确的舍入结果,其中x.5的正值向上舍入为(x + 1)以及为负值-x.5向下舍入(更负)到(x - 1)。
然而,在我看来,如果lAmount
和sDivisor
均为负数而导致阳性结果,那么舍入将是错误的,因为偏差值Quot1
会当它应该是积极的时候是消极的。结果是x.5将舍入为x而不是x + 1(如果两者都为负)。或者更糟糕的是,如果两者都是负数,则x.2将四舍五入为x - 1。
所以我想用以下内容替换上面的内容。
DCURRENCY RptRound( DCURRENCY lAmount, SHORT sDivisor )
{
D13DIGITS Quot = 0;
if (sDivisor) { /* Not Divide 0 Case */
Quot = lAmount;
Quot *= 10; // multiply by 10 with larger number of digits
Quot /= sDivisor;
// add the bias and divide now in case both lAmount and sDivisor are negative.
// this will get us to the closest whole number value of the result.
if ( Quot < 0 ) {
Quot += -5; // add half of -10 to make more negative
} else {
Quot += 5; // add half of +10 to make more positive
}
Quot /= 10; // divide by 10 to get to nearest whole number of result.
}
return Quot;
}
如果此版本sDivisor
为负且lAmount
为正,则结果将从零开始舍入(结果为负数且舍入为负数),如果sDivisor
为{否定且lAmount
为负数(结果为正数且舍入为正数)。如果sDivisor
为正数且lAmount
为正数,则结果将从零开始舍入(结果为正数且舍入为正数),如果sDivisor
为正数且lAmount
为负数,则结果从零开始舍入(结果为负数,舍入为负数)。
然而,经过一些阅读后,我对此变化的确定程度大大降低,因此我正在寻找其他反馈。
注意:,因为DCURRENCY
当前是long
,此函数会在转换返回值时生成编译器警告。一旦DCURRENCY
变为与long long
相同的D13DIGITS
,这种情况就会消失。
warning C4244: 'return' : conversion from 'D13DIGITS' to 'DCURRENCY', possible loss of data
答案 0 :(得分:3)
这里的负除数看起来有点奇怪,所以考虑一个有符号除数和无符号除数。 signed函数简单地否定了两个操作数。
不是按10缩放并加+/- 5除以10,而是加一半除数。
DCURRENCY RptRoundU(DCURRENCY lAmount, unsigned Divisor) {
if (Divisor > 0) {
if lAmount < 0) {
lAmount -= Divisor/2;
} else {
lAmount += Divisor/2;
}
lAmount /= (LONG) sDivisor;
return lAmount;
}
return 0;
}
DCURRENCY RptRoundS(DCURRENCY lAmount, signed Divisor) {
return Divisor < 0 ? RptRoundU(-lAmount, 0u-Divisor) : RptRoundU(lAmount, Divisor);
}
答案 1 :(得分:1)
这个想法只是在除数之前加上或减去除数值的一半,因为它将从零开始舍入。
为了使@ chux的答案稍微缩短,只有当其中一个值为负数时才能减去该值,即
DCURRENCY RptRound( DCURRENCY lAmount, SHORT sDivisor )
{
if (!sDivisor)
return 0; // or some invalid value
if ((sDivisor < 0) ^ (lAmount < 0)) {
lAmount -= (sDivisor / 2);
} else {
lAmount += (sDivisor / 2);
}
return lAmount / sDivisor;
}
此外,我的偏好是返回一个特殊的无效值除以零,类似于(long)0x80000000
。