programing

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

goodcopy 2022. 8. 8. 15:46
반응형

음수를 처리하는 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그러나 나머지가 마이너스인지 아닌지는 구현에 의해 정의됩니다.

레퍼런스:C++03 단락 5.6 조항 4:

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

여기서 그것은 두 개의 음의 피연산자를 모두 처리하는 버전을 따르며, 이를 통해 나머지제수에서 뺀 결과가 실제 나눗셈의 바닥이 되도록 할 수 있다. mod(-1,8)7이지만, 는 7입니다.mod(13, -8)3면 된다.

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 함수입니다.

#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)

출력:

fmodf10 10.2, 2.0) = -0.20 == FAIL!

2.0.210.2 mod 2.0 = 0.2
mod =810.2 mod -2.0 = -1.8
- 2.~10.2 mod 2.0 = 1.8
mod =2-10.2 mod -2.0 = -0.2

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

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

Microsoft Research의 논문과 참고 자료를 바탕으로 한 오래된 질문에 대한 새로운 답변입니다.

및 C로는 C11 'C++11', 'C++11', 'Da'의입니다.div0을 향한 절단이 되었습니다( 참조).[expr.mul]/4) ). 、 ) ) ). ). 。D.d C++11에 대해 을 보장합니다.qT 나머지 " " "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) || rT == 0);

서 ''는signum는 인수가 0이 아닌지에 따라 -1, 0, +1에 매핑됩니다(소스 코드에 대해서는, 이 Q&A 를 참조해 주세요).

잘린 나눗셈의 경우, 나머지의 부호는 배당의 부호와 같다.-1 % 8 == -1C++ 제공합니다.std::divquot ★★★★★★★★★★★★★★★★★」rem잘린 나눗셈에 따라

가능한 다른 정의가 있습니다. 예를 들어 플로어드 분할은 내장된 잘린 분할의 관점에서 정의될 수 있습니다.

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

플로어 나눗셈에서는 나머지의 부호는 제수의 부호와 같습니다.하스켈이나 오베론 같은 언어에는 플로어 나눗셈의 기본 연산자가 있습니다.C++에서는 위의 정의를 사용하여 함수를 작성해야 합니다.

다른 방법은 유클리드 나눗셈이며, 이것은 또한 내장된 잘린 나눗셈의 관점에서 정의될 수 있다.

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) >= 0);

유클리드 나눗셈에서, 나머지의 부호는 항상 음이 아니다.

라벨이 것을 방금 .%modulo 연산자아닌 나머지 연산자로 사용합니다.

이것은 ANSI C&C++ 사양의 정식 명칭이며, 용어의 오용도 슬금슬금 발생하고 있습니다.이거 사실 아는 사람 있어요?

그러나 이 경우 C의 fmodf() 함수(및 다른 함수)는 매우 오해의 소지가 있습니다.fremfront 등 라벨이 붙어 있어야 한다.

브랜치를 사용하지 않고 1개의 모드만 사용하는 솔루션의 경우 다음을 수행할 수 있습니다.

// Works for other sizes too,
// assuming you change 63 to the appropriate value
int64_t mod(int64_t x, int64_t div) {
  return (x % div) + (((x >> 63) ^ (div >> 63)) & div);
}

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

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

가 하는 바로는N이다.x마음에 드는 컴파일러는 이를 최적화하여 어셈블러에서 1개의 모드 조작으로 종료할 수 있어야 합니다.

수학자에게 최적의 해결책은 Python을 사용하는 것입니다.

C++ 연산자의 오버로드는 거의 관계가 없습니다.빌트인 타입의 연산자는 오버로드 할 수 없습니다.당신이 원하는 것은 단순한 기능이다.물론 C++템플릿을 사용하여 코드 1개만으로 모든 관련 유형에 해당 기능을 구현할 수 있습니다.

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서명된 유형이라고 합니다.


① 파이썬의 정수 나눗셈은 음의 무한대를 향해 반올림하기 때문이다.

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

다음과 같은 방법으로 배당을 서명되지 않은 것으로 변환할 수 있습니다.

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

result = (offset + dividend) % divider

여기서 offset은 모듈의 (-INT_MIN) 배수에 가장 가깝기 때문에 모듈을 추가하거나 빼도 모듈로는 변경되지 않습니다.부호 없는 타입을 가지며 결과는 정수입니다.유감스럽게도 INT_MIN...(-offset-1) 값은 아리페틱오버플로를 일으키기 때문에 올바르게 변환할 수 없습니다.단, 이 메서드는 일정한 나눗셈을 사용할 때 연산당 단일 추가 산술(조건 없음)만 확장되므로 DSP와 같은 애플리케이션에서 사용할 수 있습니다.

나눗셈이 2(2의 정수제곱)인N 특별한 경우가 있는데, 이 경우 다음과 같이 간단한 산술과 비트 논리를 사용하여 모듈로를 계산할 수 있습니다.

dividend&(divider-1)

예를들면

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

보다 일반적이고 덜 까다로운 방법은 이 기능을 사용하여 모듈로를 얻는 것입니다(양분할기에서만 작동).

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

음수일 경우 올바른 결과입니다.

트릭도 가능합니다.

(p%q + q)%q

매우 짧지만 일반적으로 느린 두 %-s를 사용합니다.

이 문제에 대한 또 다른 해결책은 int 대신 long형 변수를 사용하는 것이라고 생각합니다.

([0,1]에서 균일한 랜덤 변수를 생성하기 위해 [0,1]에서 음수 값을 반환하는 코드에 대해 작업 중이었는데, 변수를 long 타입으로 전환한 후 모든 것이 원활하게 실행되었고 결과는 sa 실행 시 얻은 값과 일치했습니다.me code in python (여러 플랫폼에서 동일한 "랜덤" 번호를 생성할 수 있기를 원했기 때문에 나에게 중요함)

/* 경고: 매크로 모드는 인수의 부작용을 여러 번 평가합니다.*/#sec mod(r,m) ((r) % (m) + ((r) <0)? (m) : 0)

아니면 동등한 수업의 대리인을 구하는 데 익숙해지거나.

C++ 템플릿 예시

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

이 템플릿을 사용하면 반환되는 나머지의 C++ 동작이 0이거나 배당(숫자)(0으로 반올림하는 것과 같음) 대신 반환되는 나머지가 0이거나 제수와 같은 부호(부 무한대로 반올림하는 것과 같음)가 됩니다.

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

이 솔루션(다음 경우에 사용)mod양수)를 사용하면 음수 나누기 또는 나머지 연산을 동시에 수행하지 않습니다.

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

저는 다음과 같이 하겠습니다.

((-1)+8) % 8 

이것은 원하는 대로 모듈로 giving 7을 하기 전에 첫 번째 숫자에 후자의 숫자를 더합니다.이것은 -8까지의 임의의 수치로 동작합니다.-9의 경우 2*8을 더합니다.

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

반응형