简体   繁体   English

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

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

I am working with C source code that has a function RptRound() used in various reports which takes two values, an amount and a second value such as a count, divides them and then rounds the result. 我正在使用C源代码,该源代码具有RptRound()函数,该函数已在各种报表中使用,该函数接受两个值,一个数量和一个第二个值(例如一个计数),将它们除然后取整。 The amount value being divided is usually a currency amount such as US dollars times 100 for an amount in cents. 被除以的金额值通常是货币金额,例如美元乘以100,以美分表示。

My question is whether the current function being used will provide correct rounding if the value of both lAmount and sDivisor are negative resulting in a positive result of their division. 我的问题是,如果lAmountsDivisor的值均为负,从而导致它们的除法为正,则当前使用的函数是否将提供正确的舍入。

Looking into the subject of rounding it appears that it is not nearly so cut and dried as I originally thought. 考察四舍五入的问题,似乎它没有像我最初想象的那样被切割和干燥。 However it appears the intent here was to do a Round-Half-Up type of rounding. 但是,这里的目的似乎是要进行“ Round-Half-Up”类型的舍入。 Or rather Round-Half-Away-From-Zero. 或者更确切地说,是“从零回合”。 However this function only does so if the value of the divisor, sDivisor is positive. 但是,仅当除数sDivisor值为正时,此函数才这样做。

The current function is as follows: 当前功能如下:

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);
}

It looks like so long as sDivisor is always positive then this function will give the correct, rounded up result with a positive value of x.5 rounded up to (x + 1) as well as a negative value of -x.5 rounded down (more negative) to (x - 1). 看起来只要sDivisor始终为正,则此函数将给出正确的舍入结果,将x.5的正值四舍五入为(x +1),将-x.5的负值四舍五入(更负)到(x-1)。

However it appears to me that if both the lAmount and the sDivisor are both negative resulting in a positive result then the rounding would be incorrect in that the bias value, Quot1 would be negative when it should be positive. 但是在我看来,如果lAmountsDivisor都为负,导致结果为正,则舍入将是不正确的,因为偏差值Quot1在应该为正时将为负。 The result would be that x.5 would be rounded to x and not x + 1 if both are negative. 结果将是x.5将被四舍五入为x而不是x + 1(如果两者均为负数)。 Or even worse that x.2 would be rounded to x - 1 if both are negative. 甚至更糟的是,如果x.2都为负数,则将四舍五入为x-1。

So I thought to replace the above with the following. 所以我想用以下内容代替上面的内容。

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;
}

With this version if sDivisor is negative and lAmount is positive, the result is rounded away from zero (result is negative and rounded more negative) as would the result if sDivisor is negative and lAmount is negative (result is positive and rounded more positive). 在此版本中,如果sDivisor为负且lAmount为正,则结果从零舍入(结果为负并四舍五入为负),如果sDivisor为负且lAmount为负(结果为正且四舍五入为正)。 If sDivisor is positive and lAmount is positive the result is rounded away from zero (result is positive and rounded more positive) and if sDivisor is positive and lAmount is negative the result is rounded away from zero (result is negative and rounded more negative). 如果sDivisor为正且lAmount为正,则结果将四舍五入为零(结果为正,且四舍五入为正数);如果sDivisor为正,且lAmount为负,则结果将舍入为零(结果为负,四舍五入为负)。

However my degree of certainty as to this change is considerably reduced after a bit of reading so I am looking for additional feedback. 但是,经过一番阅读后,我对该更改的确定性大大降低了,因此我正在寻找其他反馈。

Note: since DCURRENCY is currently a long this function generates a compiler warning while doing the conversion for the returned value. 注意:由于DCURRENCY当前long因此在对返回值进行转换时,此函数会生成编译器警告。 This will disappear once DCURRENCY becomes a long long which is same as D13DIGITS . 一旦DCURRENCY变为与D13DIGITS相同的long long ,它将消失。

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

A negative divisor here looks a bit odd so consider an signed one and unsigned one. 负除数在这里看起来有些奇怪,因此请考虑一个带符号的除数。 The signed function simply negates both operands. 带符号的函数仅对两个操作数求反。

Instead of scaling by 10 and adding +/-5 and dividing by 10, add half the divisor. 而不是按比例缩放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);
}

The idea is simply to add or subtract half the divisor value before dividing, as this will round away from zero. 这个想法只是在除法之前添加或减去除数的一半,因为它将舍入为零。

To make the @chux's answer slightly shorter, you can subtract the value only if one of the values is negative, ie 为了使@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;
}

Additionally, my preference would be to return a special invalid value for division by zero, something like (long)0x80000000 . 另外,我的偏好是返回一个特殊的无效值以除以零,例如(long)0x80000000

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

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