음수를 처리하는 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'의입니다.div
0을 향한 절단이 되었습니다( 참조).[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 == -1
C++ 제공합니다.std::div
를 quot
★★★★★★★★★★★★★★★★★」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
'programing' 카테고리의 다른 글
불법 반사 접근이란 무엇입니까? (0) | 2022.08.08 |
---|---|
C에 '디자인 패턴'이 있습니까? (0) | 2022.08.08 |
Java 동시성: 카운트다운 래치 vs 사이클 장벽 (0) | 2022.08.08 |
Vue.js를 사용하여 믹스인을 소품으로 전달하고 하위 컴포넌트에 사용하는 방법 (0) | 2022.08.08 |
약하게 타이핑된 언어에 관한 명백한 모순에 대한 해명을 요구하고 있다. (0) | 2022.08.08 |