C 将一个整数除以另一个值可能为负数的整数后,对结果进行整数舍入的算法
我正在使用C源代码,它有一个函数RptRound,在各种报告中使用,该函数接受两个值,一个值和第二个值(如计数),将它们相除,然后对结果进行取整。被分割的金额值通常是货币金额,例如美元乘以100,以美分表示 我的问题是,如果lAmount和sDivisor的值均为负值,则当前使用的函数是否会提供正确的舍入,从而导致其除法结果为正值 研究成圆的问题,它似乎不像我最初认为的那样简单。然而,这里的目的似乎是进行一种舍入。或者更确切地说是从零开始四舍五入。但是,此函数仅在除数sDivisor的值为正时才执行此操作 当前功能如下所示:C 将一个整数除以另一个值可能为负数的整数后,对结果进行整数舍入的算法,c,algorithm,rounding,C,Algorithm,Rounding,我正在使用C源代码,它有一个函数RptRound,在各种报告中使用,该函数接受两个值,一个值和第二个值(如计数),将它们相除,然后对结果进行取整。被分割的金额值通常是货币金额,例如美元乘以100,以美分表示 我的问题是,如果lAmount和sDivisor的值均为负值,则当前使用的函数是否会提供正确的舍入,从而导致其除法结果为正值 研究成圆的问题,它似乎不像我最初认为的那样简单。然而,这里的目的似乎是进行一种舍入。或者更确切地说是从零开始四舍五入。但是,此函数仅在除数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为负,lAmount为正,则结果从零舍入结果为负,舍入为负,就像sDivisor为负,lAmount为负一样,结果为正,舍入为正。如果sDivisor为正且lAmount为正,则结果从零舍入结果为正且舍入得更正;如果sDivisor为正且lAmount为负,则结果从零舍入结果为负且舍入得更负
然而,经过一段时间的阅读,我对这一变化的确定程度大大降低,因此我正在寻找更多的反馈
注意:由于DCURRENCY当前是一个长函数,因此该函数在对返回值进行转换时会生成编译器警告。当D电流变为与D13DIGITS相同的长-长时,这将消失
这里的负除数看起来有点奇怪,所以考虑一个已签名的和一个未签名的除数。有符号函数只是对两个操作数求反
将除数的一半加上,而不是按10进行缩放,再加上+/-5,再除以10DCURRENCY 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);
}
这里的负除数看起来有点奇怪,所以考虑一个已签名的和一个未签名的除数。有符号函数只是对两个操作数求反
将除数的一半加上,而不是按10进行缩放,再加上+/-5,再除以10DCURRENCY 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);
}
这个想法就是简单地在除法之前加上或减去除数值的一半,因为这将从零开始四舍五入 为了使@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;
}
此外,我更倾向于返回一个特殊的无效值来进行除法,比如long0x80000000。简单的想法是在除法之前加上或减去除数值的一半,因为这将从零取整 为了使@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;
}
此外,我更倾向于返回一个特殊的无效值,用于除以零,比如long0x80000000。Quot=lAmount*10;对于接近LONG_MAX/10或LONG_MIN/10的任何值,都是溢出UB。其他方法不需要*10。lAmount、sDivisor和预期结果的一些样本值将增加负面案例的清晰度。使用sDivisor<0有其他解释。依我看,为什么不使用无符号除数来避免这个问题?@chux不确定你所说的其他方法是什么意思不需要*10。你能详细说明一下吗?此外,我不确定您使用sDivisor<0的意思是否有其他解释。关于上溢和下溢的观点很好。@chux有些情况下,某些类型的数据项的计数可能为负数,因此使用无符号除数似乎不合适。您可能是正确的。然而,与10和5相比,2和1可以用于更宽的范围或下面提到的半除数技巧;对于接近LONG_MAX/10或LONG_MIN/10的任何值,都是溢出UB。其他方法不需要*10。lAmount、sDivisor和预期结果的一些样本值将增加负面案例的清晰度。使用sDivisor<0有其他解释。依我看,为什么不使用无符号除数来避免这个问题?@chux不确定你所说的其他方法是什么意思不需要*10。你能详细说明一下吗?此外,我不确定您使用sDivisor<0的意思是否有其他解释。关于上溢和下溢的观点很好。@chux有些情况下,某些类型的数据项的计数可能为负数,因此使用无符号除数似乎不合适。您可能是正确的。然而,与10和5不同,2和1可以用于更宽的范围或下面提到的半除数技巧。舍入部分可以,但if除数的其他部分!=RptRoundU函数中的0可能需要一些考虑?@Groo已更改,以匹配除数==0 case中OP的原始代码。OP正在考虑此问题。@RichardChambers
e还将处理可能发生溢出的情况,如lAmount+=除数/2;和-lAmount,但这似乎是第二个问题。@计算过程中的chux溢出/下溢是D13DIGITS变量类型为long的原因。此代码最初用于一个不支持long-long的小方框,因此定义了一个64位整数类型,使用该64位整数类型定义了一组用于64位算术的函数,然后将其转换回long。舍入部分正常,但if除数的其他部分!=RptRoundU函数中的0可能需要一些考虑?@Groo已更改,以匹配OP在除数==0的情况下的原始代码。OP正在考虑此问题。@RichardChambers迂腐代码还将处理可能发生溢出的情况,如lAmount+=除数/2;和-lAmount,但这似乎是第二个问题。@计算过程中的chux溢出/下溢是D13DIGITS变量类型为long的原因。这段代码最初用于一个不支持long-long的小盒子,因此定义了一个64位整数类型,其中包含一组函数,用于使用该64位整数类型的64位算术,然后将其转换回long。在if中使用XOR运算符非常有趣。如果尝试除以零,我不愿意更改返回值,因为我不确定报告是否希望它始终是有效的计算值,或者如果sDivisor为零,则仅为零。好的,我现在知道半除数值的原因了。@RichardChambers:如果这是您正在重构的现有遗留代码,保留0可能是有意义的,只是为了避免破坏现有功能。我如何确定从零取整是否总是合适的?我不确定这段代码是否是故意这样设计的,或者它与提供的数据一起工作只是一个意外,或者是否有人在现场遇到了不准确的情况,没有人抱怨。异或运算符在if中的有趣使用。如果尝试除以零,我不愿意更改返回值,因为我不确定报告是否希望它始终是有效的计算值,或者如果sDivisor为零,则仅为零。好的,我现在知道半除数值的原因了。@RichardChambers:如果这是您正在重构的现有遗留代码,保留0可能是有意义的,只是为了避免破坏现有功能。我如何确定从零取整是否总是合适的?我不确定这段代码是否是故意以这种方式设计的,或者它与提供的数据一起工作只是一个意外,或者是否有人在现场遇到了不准确的情况,没有人投诉。