web-dev-qa-db-ja.com

整数除算の丸め(切り捨ての代わり)

数値を最も近い整数に丸める方法を知りたいと思いました。たとえば、私が持っていた場合:

int a = 59 / 4;

浮動小数点で計算すると、14.75になります。結果を「a」に15として保存するにはどうすればよいですか?

63
Dave
int a = 59.0f / 4.0f + 0.5f;

これは、「。」以降のすべてを破棄するため、intに割り当てる場合にのみ機能します。

編集:このソリューションは、最も単純な場合にのみ機能します。より堅牢なソリューションは次のとおりです。

unsigned int round_closest(unsigned int dividend, unsigned int divisor)
{
    return (dividend + (divisor / 2)) / divisor;
}
43
0xC0DEFACE

整数の切り上げの標準的なイディオムは次のとおりです。

int a = (59 + (4 - 1)) / 4;

除数から1を引いたものを配当に追加します。

116

配当と除数のサインに有効なコード:

int divRoundClosest(const int n, const int d)
{
  return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d);
}

マクロを好む場合:

#define DIV_ROUND_CLOSEST(n, d) ((((n) < 0) ^ ((d) < 0)) ? (((n) - (d)/2)/(d)) : (((n) + (d)/2)/(d)))

LinuxカーネルマクロDIV_ROUND_CLOSESTは、負の除数では機能しません!

43
ericbn

代わりに次のようなものを使用する必要があります。

int a = (59 - 1)/ 4 + 1;

あなたは本当にもっと一般的なことをしようとしていると思います:

int divide(x, y)
{
   int a = (x -1)/y +1;

   return a;
}

x +(y-1)がオーバーフローして、誤った結果になる可能性があります。一方、x-1はx = min_int ...の場合にのみアンダーフローします.

22
WayneJ

書かれているように、整数演算を実行しています。これは、小数の結果を自動的に切り捨てます。浮動小数点演算を実行するには、定数を浮動小数点値に変更します。

_int a = round(59.0 / 4);
_

または、floatまたは他の浮動小数点型にキャストします。

_int a = round((float)59 / 4);
_

いずれにしても、_math.h_ヘッダーのround()関数を使用して最終的な丸めを行う必要があるため、必ず_#include <math.h>_にしてC99互換コンパイラーを使用してください。

7
Chris Lutz
int a, b;
int c = a / b;
if(a % b) { c++; }

剰余があるかどうかを確認すると、整数除算の商を手動で切り上げることができます。

4
user3707766

Linuxカーネル(GPLv2)から:

/*
 * Divide positive or negative dividend by positive divisor and round
 * to closest integer. Result is undefined for negative divisors and
 * for negative dividends if the divisor variable type is unsigned.
 */
#define DIV_ROUND_CLOSEST(x, divisor)(          \
{                           \
    typeof(x) __x = x;              \
    typeof(divisor) __d = divisor;          \
    (((typeof(x))-1) > 0 ||             \
     ((typeof(divisor))-1) > 0 || (__x) > 0) ?  \
        (((__x) + ((__d) / 2)) / (__d)) :   \
        (((__x) - ((__d) / 2)) / (__d));    \
}                           \
)
4
PauliusZ
#define CEIL(a, b) (((a) / (b)) + (((a) % (b)) > 0 ? 1 : 0))

別の便利なマクロ(必須):

#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
#define ABS(a)     (((a) < 0) ? -(a) : (a))
3
Magnetron

これが私の解決策です。私はそれがより読みやすく、分岐がないので(ifsも三項もありません)、それが好きです。

int32_t divide(int32_t a, int32_t b) {
  int32_t resultIsNegative = ((a ^ b) & 0x80000000) >> 31;
  int32_t sign = resultIsNegative*-2+1;
  return (a + (b / 2 * sign)) / b;
}

意図した動作を示す完全なテストプログラム:

#include <stdint.h>
#include <assert.h>

int32_t divide(int32_t a, int32_t b) {
  int32_t resultIsNegative = ((a ^ b) & 0x80000000) >> 31;
  int32_t sign = resultIsNegative*-2+1;
  return (a + (b / 2 * sign)) / b;
}

int main() {
  assert(divide(0, 3) == 0);

  assert(divide(1, 3) == 0);
  assert(divide(5, 3) == 2);

  assert(divide(-1, 3) == 0);
  assert(divide(-5, 3) == -2);

  assert(divide(1, -3) == 0);
  assert(divide(5, -3) == -2);

  assert(divide(-1, -3) == 0);
  assert(divide(-5, -3) == 2);
}

私が好む@ericbnから借りる

#define DIV_ROUND_INT(n,d) ((((n) < 0) ^ ((d) < 0)) ? (((n) - (d)/2)/(d)) : (((n) + (d)/2)/(d)))
or if you work only with unsigned ints
#define DIV_ROUND_UINT(n,d) ((((n) + (d)/2)/(d)))
2
zevero
int divide(x,y){
 int quotient = x/y;
 int remainder = x%y;
 if(remainder==0)
  return quotient;
 int tempY = divide(y,2);
 if(remainder>=tempY)
  quotient++;
 return quotient;
}

例:59/4商= 14、tempY = 2、剰余= 3、剰余> = tempYしたがって商= 15。

1
Abhay Lolekar
double a=59.0/4;
int b=59/4;
if(a-b>=0.5){
    b++;
}
printf("%d",b);
  1. 59.0/4の正確なフロート値をxとする(ここでは14.750000)
  2. xより小さい最小の整数をyとする(ここでは14)
  3. x-y <0.5の場合、yは解です
  4. それ以外の場合はy + 1が解決策です
1
Siva Dhatra

正の整数を除算する場合は、上にシフトして除算し、実b0の右側のビットをチェックできます。つまり、100/8は12.5ですが、12を返します。(100 << 1)/ 8を行うと、b0をチェックして、結果をシフトダウンしてから切り上げることができます。

0
bryan

切り上げを行う数学関数を使用してみてください。 Math Ceil

0
Samuel Santos

以下は、正または負のオペランドを浮動小数点または条件分岐なしで最も近い整数に商を正しく丸めます(以下のアセンブリ出力を参照)。 Nビットの2の補数の整数を想定しています。

#define ASR(x) ((x) < 0 ? -1 : 0)  // Compiles into a (N-1)-bit arithmetic shift right
#define ROUNDING(x,y) ( (y)/2 - (ASR((x)^(y)) & (y)))

int RoundedQuotient(int x, int y)
   {
   return (x + ROUNDING(x,y)) / y ;
   }

ROUNDINGの値には、被除数(x)と同じ符号があり、除数(-)のmagnitudeの半分になります。したがって、配当にROUNDINGを追加すると、整数の除算が結果の商を切り捨てる前に、その大きさが増加します。 32ビットARM Cortex-M4プロセッサの-O3最適化を使用したgccコンパイラの出力は次のとおりです。

RoundedQuotient:                // Input parameters: r0 = x, r1 = y
    eor     r2, r1, r0          // r2 = x^y
    and     r2, r1, r2, asr #31 // r2 = ASR(x^y) & y
    add     r3, r1, r1, lsr #31 // r3 = (y < 0) ? y + 1 : y
    rsb     r3, r2, r3, asr #1  // r3 = y/2 - (ASR(x^y) & y)
    add     r0, r0, r3          // r0 = x + (y/2 - (ASR(x^y) & y)
    sdiv    r0, r0, r1          // r0 = (x + ROUNDING(x,y)) / y
    bx      lr                  // Returns r0 = rounded quotient
0
Dan Lewis

4で割るためのいくつかの選択肢

return x/4 + (x/2 % 2);
return x/4 + (x % 4 >= 2)

または一般的に、2の累乗による除算

return x/y + x/(y/2) % 2;    // or
return (x >> i) + ((x >> i - 1) & 1);  // with y = 2^i

小数部が0.5、つまり最初の桁がbase/2の場合は切り上げます。バイナリでは、結果に最初の小数ビットを追加することと同等です

キャリーフラグには最後にシフトアウトされたビットが含まれるため、この方法にはフラグレジスタを使用するアーキテクチャで利点があります。たとえば、x86では optimized into になります。

shr eax, i
adc eax, 0

また、符号付き整数をサポートするように簡単に拡張できます。負の数の式は

(x - 1)/y + ((x - 1)/(y/2) & 1)

私たちはそれを正と負の値の両方で動作させることができます

int t = x + (x >> 31);
return (t >> i) + ((t >> i - 1) & 1);
0
phuclv

一部のアルゴリズムでは、「最近接」が同点の場合に一貫したバイアスが必要です。

_// round-to-nearest with mid-value bias towards positive infinity
int div_nearest( int n, int d )
   {
   if (d<0) n*=-1, d*=-1;
   return (abs(n)+((d-(n<0?1:0))>>1))/d * ((n<0)?-1:+1);
   }
_

これは、分子または分母の符号に関係なく機能します。


round(N/(double)D)(浮動小数点除算と丸め)の結果を一致させたい場合、すべて同じ結果を生成するいくつかのバリエーションがあります。

_int div_nearest( int n, int d )
   {
   int r=(n<0?-1:+1)*(abs(d)>>1); // eliminates a division
// int r=((n<0)^(d<0)?-1:+1)*(d/2); // basically the same as @ericbn
// int r=(n*d<0?-1:+1)*(d/2); // small variation from @ericbn
   return (n+r)/d;
   }
_

注:_(abs(d)>>1)_対_(d/2)_の相対速度は、プラットフォームに依存する可能性があります。

0
nobar