繁体   English   中英

将一个整数除以值可能为负的整数后结果的整数舍入算法

[英]Algorithm for integer rounding of result after division of one integer by another whose values may be negative

我正在使用C源代码,该源代码具有RptRound()函数,该函数已在各种报表中使用,该函数接受两个值,一个数量和一个第二个值(例如一个计数),将它们除然后取整。 被除以的金额值通常是货币金额,例如美元乘以100,以美分表示。

我的问题是,如果lAmountsDivisor的值均为负,从而导致它们的除法为正,则当前使用的函数是否将提供正确的舍入。

考察四舍五入的问题,似乎它没有像我最初想象的那样被切割和干燥。 但是,这里的目的似乎是要进行“ 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)。

但是在我看来,如果lAmountsDivisor都为负,导致结果为正,则舍入将是不正确的,因为偏差值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变为与D13DIGITS相同的long long ,它将消失。

warning C4244: 'return' : conversion from 'D13DIGITS' to 'DCURRENCY', possible loss of data

负除数在这里看起来有些奇怪,因此请考虑一个带符号的除数。 带符号的函数仅对两个操作数求反。

而不是按比例缩放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);
}

这个想法只是在除法之前添加或减去除数的一半,因为它将舍入为零。

为了使@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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM