(수학자로서) C 파생 언어를 싫어하는 것 중 하나는
(-1) % 8 // comes out as -1, and not 7
fmodf(-1,8) // fails similarly
최상의 솔루션은 무엇입니까?
C ++는 템플릿과 연산자 오버로딩의 가능성을 허용하지만이 두 가지 모두 저에게 어둡습니다. 사례를 감사하게 받았습니다.
(수학자로서) C 파생 언어를 싫어하는 것 중 하나는
(-1) % 8 // comes out as -1, and not 7
fmodf(-1,8) // fails similarly
최상의 솔루션은 무엇입니까?
C ++는 템플릿과 연산자 오버로딩의 가능성을 허용하지만이 두 가지 모두 저에게 어둡습니다. 사례를 감사하게 받았습니다.
%
는 모듈로 라고합니다 ... 나머지 입니다.
%
문제 에 대한 참조 용 입니다.
(-1) & 8 == 7
답변:
먼저 (-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와 같습니다. 두 피연산자가 음수가 아니면 나머지는 음수가 아닙니다. 그렇지 않은 경우 나머지 부호는 구현 정의 입니다.
INT_MIN / -1
(2의 보수 구현에서) 문제를 야기합니다 . 이전 사양에서는 ID를 유지하기 위해 (16 비트 유형의 범위에 있지 않습니다. yuck!) -32768 % -1
평가해야 할 수 있습니다 -65536
.
다음은 두 연산에 대해 양수 또는 음의 정수 또는 분수 값을 처리하는 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
floor()
. 또한 float로 변환 할 때 정밀도가 떨어질 수 있습니다. Try (float)1000000001/3
, 결과에 놀랄 것입니다!
방금 Bjarne Stroustrup 이 모듈로 연산자가 아닌 나머지 연산자 %
로 레이블 을 지정하는 것을 확인했습니다 .
나는 이것이 ANSI C & C ++ 사양의 공식적인 이름이고 용어의 남용이 들어 왔다고 확신합니다. 누구든지 이것을 사실로 알고 있습니까?
그러나 이것이 사실이라면 C의 fmodf () 함수 (그리고 아마도 다른 것)는 매우 오해의 소지가 있습니다. fremf () 등으로 표시되어야합니다.
%
) 와 관련하여 "모듈로"를 한 번도 언급하지 않습니다 .
양수 모듈로를 찾는 가장 간단한 일반 함수는 다음과 같습니다. x의 양수 값과 음수 값 모두에서 작동합니다.
int modulo(int x,int N){
return (x % N + N) %N;
}
정수의 경우 이것은 간단합니다. 그냥 해
(((x < 0) ? ((x % N) + N) : x) % N)
나는 그것이 N
긍정적이고 x
. 여러분이 가장 좋아하는 컴파일러는 이것을 최적화 할 수 있어야합니다. 그래서 어셈블러에서 단 하나의 모드 작업으로 끝납니다.
int x=-9001; unsigned int N=2000;
그렇지 않은 999 2295, 제공
(x < 0) ? (x % N + N) : (x % N)
.
수학자에게 최상의 솔루션 ¹은 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의 정수 나눗셈은 음의 무한대로 반올림되기 때문입니다.
r
결과를 확인한다 a
=이 r + b*(a/b)
사실. 정수 나눗셈이 어떻게 구현 되든 상관없이 b*something
의 배수입니다 b
. 이것은 r
음수 일지라도 유효한 모듈로 결과를 만듭니다 . abs ( b
)를 추가 할 수 있으며 여전히 유효한 모듈로 결과입니다.
아, 이것도 % 디자인이 싫어 ....
다음과 같은 방법으로 배당금을 부호없는 것으로 변환 할 수 있습니다.
unsigned int offset = (-INT_MIN) - (-INT_MIN)%divider
result = (offset + dividend) % divider
여기서 오프셋은 모듈의 배수 (-INT_MIN)에 가장 가깝기 때문에 더하거나 빼도 모듈로가 변경되지 않습니다. unsigned 유형이 있으며 결과는 정수입니다. 불행히도 INT_MIN ... (-offset-1) 값을 올바르게 변환 할 수 없습니다. 그러나이 방법은 상수 분배기로 작업 할 때 연산 당 하나의 추가 산술 만 (조건부 없음) 장점이 있으므로 DSP와 유사한 응용 프로그램에서 사용할 수 있습니다.
분할기가 2N ( 2의 정수 거듭 제곱) 인 특수한 경우 가 있습니다. 모듈로는 다음과 같이 간단한 산술 및 비트 논리를 사용하여 계산할 수 있습니다.
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를 사용합니다.
다음은이 Microsoft Research 문서 와 그 안의 참조를 기반으로 한 이전 질문에 대한 새로운 답변 입니다.
C11 및 C ++ (11) 이후의 의미론에서 참고 div
되었다 제로 향해 절단 (참조 [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));
여기서, signum
맵의 인수 여부에 따라 -1, 0, +1, 행 <==> 0 (참조 이상 이 Q & A 소스 코드를).
잘린 나눗셈을 사용 하면 나머지 부호는 피제수 부호와 같습니다D
-1 % 8 == -1
. 즉 . C ++ 11은 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));
내림 나눗셈 의 경우 나머지 부호는 제수 부호와 같습니다d
. Haskell 및 Oberon과 같은 언어에는 플로어 디비전을위한 내장 연산자가 있습니다. 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) != -1);
유클리드 나눗셈 을 사용하면 나머지 부호는 항상 양수 입니다.
assert(signum(rT) == signum(D));
확실히 실패 할 수 있습니다. 올바른 설명 : signum(rT)
세트 { 0
, signum(D)
} 의 구성원 이거나 주장으로assert(rT == 0 || signum(rT) == signum(D));
D = 10
andd = 5
/ * 경고 : 매크로 모드는 인수의 부작용을 여러 번 평가합니다. * / # mod (r, m) 정의 (((r) % (m)) + ((r) <0)? (m) : 0)
... 또는 동등 클래스의 대표자를 얻는 데 익숙해집니다.
r
. %
연산자는 등가 클래스와는 아무 상관이있다. 나머지 연산자이고 나머지는 음수가 아니고 제수보다 작도록 대수적으로 잘 정의되어 있습니다. 슬프게도 C는 그것을 잘못된 방식으로 정의했습니다. 그래도 최고의 답변 중 하나가 있으면 +1합니다.
define MOD(a, b) ((((a)%(b))+(b))%(b))
난 그럴거야:
((-1)+8) % 8
이것은 원하는대로 7을 제공하는 모듈로를 수행하기 전에 첫 번째 숫자에 후자의 숫자를 추가합니다. 이것은 -8까지의 숫자에 대해 작동합니다. -9의 경우 2 * 8을 추가하십시오.
-99999
?