program story

음수를 처리하는 C / C ++ / Obj-C에서 모듈로 (%) 연산자를 코딩하는 방법

inputbox 2020. 10. 8. 07:58
반응형

음수를 처리하는 C / C ++ / Obj-C에서 모듈로 (%) 연산자를 코딩하는 방법


(수학자로서) C 파생 언어를 싫어하는 것 중 하나는

(-1) % 8 // comes out as -1, and not 7

fmodf(-1,8) // fails similarly

최상의 솔루션은 무엇입니까?

C ++는 템플릿과 연산자 오버로딩의 가능성을 허용하지만이 두 가지 모두 저에게 어둡습니다. 예를 감사하게 받았습니다.


먼저 (-1) % 8 == -1. 당신이 의지 할 수있는 유일한 것은 (x / y) * y + ( x % y) == x. 그러나 나머지가 음수인지 여부는 구현에 따라 정의됩니다 .

이제 여기서 템플릿을 사용하는 이유는 무엇입니까? int 및 longs에 대한 과부하가 발생합니다.

int mod (int a, int b)
{
   int ret = a % b;
   if(ret < 0)
     ret+=b;
   return ret;
}

이제 mod (-1,8)처럼 호출 할 수 있으며 7로 표시됩니다.

편집 : 코드에서 버그를 발견했습니다. b가 음수이면 작동하지 않습니다. 그래서 나는 이것이 더 낫다고 생각합니다.

int mod (int a, int b)
{
   if(b < 0) //you can check for b == 0 separately and do what you want
     return mod(a, -b);   
   int ret = a % b;
   if(ret < 0)
     ret+=b;
   return ret;
}

참조 : C ++ 03 단락 5.6 조항 4 :

이항 / 연산자는 몫을 산출하고 이항 % 연산자는 첫 번째 표현식을 두 번째로 나눈 나머지를 산출합니다. / 또는 %의 두 번째 피연산자가 0이면 동작이 정의되지 않습니다. 그렇지 않으면 (a / b) * b + a % b는 a와 같습니다. 두 피연산자가 음수가 아니면 나머지는 음수가 아닙니다. 그렇지 않은 경우 나머지 부호는 구현 정의 입니다.


다음은 두 연산에 대해 양수 또는 음의 정수 또는 분수 값을 처리하는 C 함수입니다.

#include <math.h>
float mod(float a, float N) {return a - N*floor(a/N);} //return in range [0, N)

이것은 확실히 수학적 관점에서 가장 우아한 해결책입니다. 그러나 정수 처리에 강력한 지 확실하지 않습니다. int-> fp-> int를 변환 할 때 가끔 부동 소수점 오류가 발생합니다.

이 코드는 int가 아닌 경우에 사용하고 int에는 별도의 함수를 사용하고 있습니다.

참고 : N = 0을 트랩해야합니다!

테스터 코드 :

#include <math.h>
#include <stdio.h>

float mod(float a, float N)
{
    float ret = a - N * floor (a / N);

    printf("%f.1 mod %f.1 = %f.1 \n", a, N, ret);

    return ret;
}

int main (char* argc, char** argv)
{
    printf ("fmodf(-10.2, 2.0) = %f.1  == FAIL! \n\n", fmodf(-10.2, 2.0));

    float x;
    x = mod(10.2f, 2.0f);
    x = mod(10.2f, -2.0f);
    x = mod(-10.2f, 2.0f);
    x = mod(-10.2f, -2.0f);

    return 0;
}

(참고 : CodePad에서 바로 컴파일하고 실행할 수 있습니다 : http://codepad.org/UOgEqAMA )

산출:

fmodf (-10.2, 2.0) = -0.20 == 실패!

10.2 mod 2.0 = 0.2
10.2 mod -2.0 = -1.8
-10.2 mod 2.0 = 1.8
-10.2 mod -2.0 = -0.2


방금 Bjarne Stroustrup 이 모듈로 연산자가 아닌 나머지 연산자 %레이블 을 지정하는 것을 확인했습니다 .

나는 이것이 ANSI C & C ++ 사양의 공식적인 이름이고 용어의 남용이 들어 왔다고 확신합니다. 누구든지 이것을 사실로 알고 있습니까?

그러나 이것이 사실이라면 C의 fmodf () 함수 (그리고 아마도 다른 것)는 매우 오해의 소지가 있습니다. fremf () 등으로 표시되어야합니다.


정수의 경우 이것은 간단합니다. 그냥 해

(((x < 0) ? ((x % N) + N) : x) % N)

나는 그것이 N긍정적이고 x. 여러분이 가장 좋아하는 컴파일러는이를 최적화 할 수 있어야 어셈블러에서 단 하나의 모드 작업으로 끝납니다.


수학자에게 최상의 솔루션 ¹은 Python을 사용하는 것입니다.

C ++ 연산자 오버로딩은 이와 거의 관련이 없습니다. 기본 제공 형식에 대해서는 연산자를 오버로드 할 수 없습니다. 원하는 것은 단순히 함수입니다. 물론 C ++ 템플릿을 사용하여 단 하나의 코드로 모든 관련 유형에 대해 해당 함수를 구현할 수 있습니다.

표준 C 라이브러리는 fmod부동 소수점 유형에 대해 이름을 올바르게 기억하면를 제공합니다 .

정수의 경우 항상 음이 아닌 나머지 (유클리드 나눗셈에 해당)를 반환하는 C ++ 함수 템플릿을 ...

#include <stdlib.h>  // abs

template< class Integer >
auto mod( Integer a, Integer b )
    -> Integer
{
    Integer const r = a%b;
    return (r < 0? r + abs( b ) : r);
}

... mod(a, b)대신 작성하십시오 a%b.

여기서 유형 Integer은 부호있는 정수 유형이어야합니다.

나머지 부호가 제수 부호와 동일한 일반적인 수학 동작을 원한다면 다음과 같이 할 수 있습니다.

template< class Integer >
auto floor_div( Integer const a, Integer const b )
    -> Integer
{
    bool const a_is_negative = (a < 0);
    bool const b_is_negative = (b < 0);
    bool const change_sign  = (a_is_negative != b_is_negative);

    Integer const abs_b         = abs( b );
    Integer const abs_a_plus    = abs( a ) + (change_sign? abs_b - 1 : 0);

    Integer const quot = abs_a_plus / abs_b;
    return (change_sign? -quot : quot);
}

template< class Integer >
auto floor_mod( Integer const a, Integer const b )
    -> Integer
{ return a - b*floor_div( a, b ); }

…에 대해 동일한 제약 조건을 적용 Integer하여 서명 된 유형입니다.


¹ Python의 정수 나눗셈은 음의 무한대로 반올림되기 때문입니다.


양수 모듈로를 찾는 가장 간단한 일반 함수는 다음과 같습니다. x의 양수 값과 음수 값 모두에서 작동합니다.

int modulo(int x,int N){
    return (x % N + N) %N;
}

아, 이것도 % 디자인이 싫어 ....

다음과 같은 방법으로 배당금을 부호없는 것으로 변환 할 수 있습니다.

unsigned int offset = (-INT_MIN) - (-INT_MIN)%divider

result = (offset + dividend) % divider

where offset is closest to (-INT_MIN) multiple of module, so adding and subtracting it will not change modulo. Note that it have unsigned type and result will be integer. Unfortunately it cannot correctly convert values INT_MIN...(-offset-1) as they cause arifmetic overflow. But this method have advandage of only single additional arithmetic per operation (and no conditionals) when working with constant divider, so it is usable in DSP-like applications.

There's special case, where divider is 2N (integer power of two), for which modulo can be calculated using simple arithmetic and bitwise logic as

dividend&(divider-1)

for example

x mod 2 = x & 1
x mod 4 = x & 3
x mod 8 = x & 7
x mod 16 = x & 15

More common and less tricky way is to get modulo using this function (works only with positive divider):

int mod(int x, int y) {
    int r = x%y;
    return r<0?r+y:r;
}

This just correct result if it is negative.

Also you may trick:

(p%q + q)%q

It is very short but use two %-s which are commonly slow.


I believe another solution to this problem would be use to variables of type long instead of int.

I was just working on some code where the % operator was returning a negative value which caused some issues (for generating uniform random variables on [0,1] you don't really want negative numbers :) ), but after switching the variables to type long, everything was running smoothly and the results matched the ones I was getting when running the same code in python (important for me as I wanted to be able to generate the same "random" numbers across several platforms.


/* Warning: macro mod evaluates its arguments' side effects multiple times. */
#define mod(r,m) (((r) % (m)) + ((r)<0)?(m):0)

... or just get used to getting any representative for the equivalence class.


Here's a new answer to an old question, based on this Microsoft Research paper and references therein.

Note that from C11 and C++11 onwards, the semantics of div has become truncation towards zero (see [expr.mul]/4). Furthermore, for D divided by d, C++11 guarantees the following about the quotient qT and remainder rT

auto const qT = D / d;
auto const rT = D % d;
assert(D == d * qT + rT);
assert(abs(rT) < abs(d));
assert(signum(rT) == signum(D));

where signum maps to -1, 0, +1, depending on whether its argument is <, ==, > than 0 (see this Q&A for source code).

With truncated division, the sign of the remainder is equal to the sign of the dividend D, i.e. -1 % 8 == -1. C++11 also provides a std::div function that returns a struct with members quot and rem according to truncated division.

There are other definitions possible, e.g. so-called floored division can be defined in terms of the builtin truncated division

auto const I = signum(rT) == -signum(d) ? 1 : 0;
auto const qF = qT - I;
auto const rF = rT + I * d;
assert(D == d * qF + rF);
assert(abs(rF) < abs(d));
assert(signum(rF) == signum(d));

With floored division, the sign of the remainder is equal to the sign of the divisor d. In languages such as Haskell and Oberon, there are builtin operators for floored division. In C++, you'd need to write a function using the above definitions.

Yet another way is Euclidean division, which can also be defined in terms of the builtin truncated division

auto const I = rT >= 0 ? 0 : (d > 0 ? 1 : -1);
auto const qE = qT - I;
auto const rE = rT + I * d;
assert(D == d * qE + rE);
assert(abs(rE) < abs(d));
assert(signum(rE) != -1);

With Euclidean division, the sign of the remainder is always positive.


Example template for C++

template< class T >
T mod( T a, T b )
{
    T const r = a%b;
    return ((r!=0)&&((r^b)<0) ? r + b : r);
}

With this template, the returned remainder will be zero or have the same sign as the divisor (denominator) (the equivalent of rounding towards negative infinity), instead of the C++ behavior of the remainder being zero or having the same sign as the dividend (numerator) (the equivalent of rounding towards zero).


define  MOD(a, b)       ((((a)%(b))+(b))%(b))

unsigned mod(int a, unsigned b) {
    return (a >= 0 ? a % b : b - (-a) % b);
}

This solution (for use when mod is positive) avoids taking negative divide or remainder operations all together:

int core_modulus(int val, int mod)
{
    if(val>=0)
        return val % mod;
    else
        return val + mod * ((mod - val - 1)/mod);
}

I would do:

((-1)+8) % 8 

This adds the latter number to the first before doing the modulo giving 7 as desired. This should work for any number down to -8. For -9 add 2*8.

참고URL : https://stackoverflow.com/questions/4003232/how-to-code-a-modulo-operator-in-c-c-obj-c-that-handles-negative-numbers

반응형