>>> (float('inf')+0j)*1
(inf+nanj)
왜? 이것은 내 코드에서 불쾌한 버그를 일으켰습니다.
1
곱셈 적 정체성 은 왜 주지 (inf + 0j)
않습니까?
>>> (float('inf')+0j)*1
(inf+nanj)
왜? 이것은 내 코드에서 불쾌한 버그를 일으켰습니다.
1
곱셈 적 정체성 은 왜 주지 (inf + 0j)
않습니까?
답변:
는 1
먼저 복소수로 변환되고 1 + 0j
, 이는 내지 An 후 리드 inf * 0
A의 결과 곱셈 nan
.
(inf + 0j) * 1
(inf + 0j) * (1 + 0j)
inf * 1 + inf * 0j + 0j * 1 + 0j * 0j
# ^ this is where it comes from
inf + nan j + 0j - 0
inf + nan j
1
is cast to 1 + 0j
.
array([inf+0j])*1
에서도 array([inf+nanj])
. 실제 곱셈이 C / C ++ 코드 어딘가에서 발생한다고 가정하면 _Complex 또는 std :: complex를 사용하는 대신 CPython 동작을 에뮬레이트하기 위해 사용자 지정 코드를 작성했음을 의미합니까?
numpy
하나의 중앙 클래스가 ufunc
있습니다. ufunc
방송 관리는 어레이 작업을 매우 편리하게 만드는 까다로운 관리자를 모두 처리합니다. 보다 정확하게는 특정 연산자와 일반 기계 간의 노동 분할은 특정 연산자가 처리하려는 입력 및 출력 요소 유형의 각 조합에 대해 "가장 안쪽 루프"세트를 구현한다는 것입니다. 일반 기계는 ... 어떤 외부 루프를 돌봐 및 루프 안쪽에 최적의 일치를 선택
types
속성을 통해 제공된 내부 루프 목록에 액세스 할 수 있습니다. 혼합 유형이 거의 없음을 알 수 있습니다. 특히 float 과 complex를 혼합하는 유형은 없습니다 . np.multiply
['??->?', 'bb->b', 'BB->B', 'hh->h', 'HH->H', 'ii->i', 'II->I', 'll->l', 'LL->L', 'qq->q', 'QQ->Q', 'ee->e', 'ff->f', 'dd->d', 'gg->g', 'FF->F', 'DD->D', 'GG->G', 'mq->m', 'qm->m', 'md->m', 'dm->m', 'OO->O']
"efdg"
"FDG"
기계적으로 받아 들여진 대답은 물론 옳지 만 더 깊은 대답이 주어질 수 있다고 주장합니다.
첫째, @PeterCordes가 주석에서하는 것처럼 질문을 명확히하는 것이 유용합니다. "inf + 0j에서 작동하는 복소수에 대한 곱셈 동일성이 있습니까?" 즉, OP가 복잡한 곱셈의 컴퓨터 구현에서 약점을 보거나 개념적으로 불건전 한 것이 있는가inf+0j
극좌표를 사용하여 복잡한 곱셈을 배율 및 회전으로 볼 수 있습니다. 1을 곱하는 경우처럼 무한한 "팔"을 0도 회전 시키면 끝이 유한 한 정밀도로 배치 될 것으로 기대할 수 없습니다. 따라서 실제로에는 근본적으로 옳지 않은 것이 있습니다 inf+0j
. 즉, 무한대에 도달하자마자 유한 오프셋이 무의미 해집니다.
배경 :이 질문이 돌아가는 "큰 문제"는 숫자 체계를 확장하는 문제입니다 (실수 또는 복소수를 생각하십시오). 그렇게하고 싶은 한 가지 이유는 무한의 개념을 추가하거나 수학자라면 "압축"하기 위해서입니다. 다른 이유도 있지만 ( https://en.wikipedia.org/wiki/Galois_theory , https://en.wikipedia.org/wiki/Non-standard_analysis ) 여기서는 관심이 없습니다.
이러한 확장에 대한 까다로운 부분은 물론 이러한 새 숫자가 기존 산술에 적합하기를 원한다는 것입니다. 가장 간단한 방법은 무한대 ( https://en.wikipedia.org/wiki/Alexandroff_extension )에 단일 요소를 추가하고 0을 0으로 나눈 값과 동일하게 만드는 것입니다. 이것은 실수 ( https://en.wikipedia.org/wiki/Projectively_extended_real_line )와 복소수 ( https://en.wikipedia.org/wiki/Riemann_sphere )에서 작동합니다.
원 포인트 압축은 간단하고 수학적으로 건전하지만 여러 무한대를 포함하는 "더 풍부한"확장이 추구되었습니다. 실제 부동 소수점 숫자에 대한 IEEE 754 표준에는 + inf 및 -inf ( https://en.wikipedia.org/wiki/Extended_real_number_line )가 있습니다. 자연스럽고 간단 해 보이지만 이미 우리가 https://en.wikipedia.org/wiki/Signed_zero 와 같은 것을 발명하고 농구를하도록 강요합니다.-0
복잡한 평면의 1 개 이상의 inf 확장은 어떻습니까?
컴퓨터에서 복소수는 일반적으로 실수 부분과 허수 부분에 대해 하나씩 두 개의 fp 실수를 붙임으로써 구현됩니다. 모든 것이 유한 한 한 완벽하게 괜찮습니다. 그러나 무한대가 고려됨에 따라 상황이 까다로워집니다.
복잡한 평면은 자연적인 회전 대칭을 가지며, 전체 평면에 e ^ phij를 곱하는 것은 phi 라디안 회전과 동일하므로 복잡한 산술과 잘 연결됩니다 0
.
이제 단순하게 유지하기 위해 복잡한 fp는 기본 실수 구현의 확장 (+/- inf, nan 등)을 사용합니다. 이 선택은 너무 자연스러워서 선택으로 인식되지 않을 수도 있지만 그것이 의미하는 바를 자세히 살펴 보겠습니다. 복잡한 평면의 확장에 대한 간단한 시각화는 다음과 같습니다 (I = 무한, f = 유한, 0 = 0).
I IIIIIIIII I
I fffffffff I
I fffffffff I
I fffffffff I
I fffffffff I
I ffff0ffff I
I fffffffff I
I fffffffff I
I fffffffff I
I fffffffff I
I IIIIIIIII I
그러나 진정한 복잡한 평면은 복잡한 곱셈을 고려하는 평면이므로 더 유익한 투영은
III
I I
fffff
fffffff
fffffffff
I fffffffff I
I ffff0ffff I
I fffffffff I
fffffffff
fffffff
fffff
I I
III
이 예측에서 우리는 추악 할뿐만 아니라 OP 종류의 문제의 근원 인 무한대의 "불균등 한 분포"를 볼 수 있습니다. 대부분의 무한대 (형식 (+/- inf, 유한) 및 (유한, + / -inf)는 4 개의 주요 방향으로 함께 묶여 있습니다. 다른 모든 방향은 4 개의 무한대 (+/- inf, + -inf)로 표현됩니다. 복잡한 곱셈을이 기하학으로 확장하는 것이 악몽이라는 것은 놀라운 일이 아닙니다. .
C99 사양의 Annex G는 작동 방식 inf
및 nan
상호 작용 에 대한 규칙을 변경하는 것을 포함하여 최선을 다하고 있습니다 (본질적으로 inf
우선 nan
). OP의 문제는 실수와 제안 된 순수 상상의 유형을 복합으로 승격시키지 않음으로써 회피되지만 실제 1이 복합 1과 다르게 행동하는 것은 해결책으로 생각되지 않습니다. 분명히 Annex G는 두 무한대의 곱이 무엇인지 완전히 지정하지 못합니다.
더 나은 무한 기하학을 선택하여 이러한 문제를 해결하려는 유혹이 있습니다. 확장 된 실제 선과 유사하게 각 방향에 대해 하나의 무한대를 추가 할 수 있습니다. 이 구조는 투영 평면과 비슷하지만 반대 방향으로 함께 뭉치지는 않습니다. 무한대는 극좌표 inf xe ^ {2 omega pi i}로 표현되며, 제품을 정의하는 것은 간단합니다. 특히 OP의 문제는 자연스럽게 해결 될 것입니다.
그러나 이것이 좋은 소식이 끝나는 곳입니다. 어떤면에서 우리는 우리의 새로운 스타일 무한대가 실제 또는 가상 부분을 추출하는 기능을 지원하도록 요구함으로써 정점으로 돌아갈 수 있습니다. 추가는 또 다른 문제입니다. 두 개의 nonantipodal 무한대를 추가하면 각도를 정의되지 않음으로 설정 nan
해야합니다. 즉 , 각도가 두 입력 각도 사이에 있어야한다고 주장 할 수 있지만 "부분적 난감"을 나타내는 간단한 방법은 없습니다.
이 모든 점을 고려할 때 좋은 오래된 1 점 압축이 가장 안전한 방법 일 수 있습니다. 아마도 Annex G의 저자는 cproj
모든 무한대를 하나로 묶는 기능 을 명령 할 때 똑같이 느꼈을 것입니다.
여기에 저 보다 주제에 대해 더 유능한 사람들이 대답 한 관련 질문이 있습니다.
nan != nan
. 이 답변이 반 농담이라는 것을 이해하지만 작성 된 방식으로 OP에 도움이되어야하는 이유를 알지 못합니다.
==
다른 답변을 수락 한 경우 OP가 제목을 어떻게 표현했는지에 대한 문제인 것 같습니다. 나는 그 불일치를 수정하기 위해 제목을 변경했습니다. (저는 @cmaster에 동의하기 때문에이 답변의 전반부를 의도적으로 무효화합니다.이 질문이 묻는 것이 아닙니다).
이것은 CPython에서 복잡한 곱셈이 구현되는 방법에 대한 구현 세부 사항입니다. 다른 언어 (예 : C 또는 C ++)와 달리 CPython은 다소 단순한 접근 방식을 취합니다.
Py_complex
_Py_c_prod(Py_complex a, Py_complex b)
{
Py_complex r;
r.real = a.real*b.real - a.imag*b.imag;
r.imag = a.real*b.imag + a.imag*b.real;
return r;
}
위의 코드에서 문제가되는 경우는 다음과 같습니다.
(0.0+1.0*j)*(inf+inf*j) = (0.0*inf-1*inf)+(0.0*inf+1.0*inf)j
= nan + nan*j
그러나 -inf + inf*j
결과적 으로 갖고 싶습니다 .
이 점에서 다른 언어는 멀지 않습니다. 복소수 곱셈은 C 표준의 일부가 아니라 오랫동안 C 표준의 일부가 아니 었습니다. C99에는 복잡한 곱셈이 어떻게 수행되어야하는지 설명하는 부록 G로만 포함되어 있습니다. 위의 학교 공식! C ++ 표준은 복잡한 곱셈의 작동 방식을 지정하지 않으므로 대부분의 컴파일러 구현은 C99 준수 (gcc, clang) 또는 그렇지 않은 (MSVC) C 구현으로 대체됩니다.
위의 "문제가있는"예의 경우 C99 준수 구현 ( 학교 공식보다 더 복잡함 )은 예상 결과를 제공합니다 ( live 참조 ).
(0.0+1.0*j)*(inf+inf*j) = -inf + inf*j
C99 표준을 사용하더라도 모든 입력에 대해 명확한 결과가 정의되지 않으며 C99 호환 버전에서도 다를 수 있습니다.
다른 부작용 float
으로 승격되지 complex
C99에 곱한이다 inf+0.0j
으로 1.0
또는하면 1.0+0.0j
(여기서는 실시간 참조) 다른 결과가 발생할 수있다 :
(inf+0.0j)*1.0 = inf+0.0j
(inf+0.0j)*(1.0+0.0j) = inf-nanj
, 가상의 일원 -nan
이 아닌 nan
모든 조용한에 NaN을가합니다 (동일하기 때문에 (CPython을위한 등)이 여기에 역할을하지 않습니다 이 )도 그들 중 일부는 부호 비트가 설정되어 (따라서로 인쇄, "-"볼 이 ) 일부는 아닙니다.적어도 반 직관적입니다.
여기서 중요한 점은 "단순한"복소수 곱셈 (또는 나눗셈)에 대한 단순한 것이 없으며 언어 나 컴파일러간에 전환 할 때 미묘한 버그 / 차이에 대비해야한다는 것입니다.
printf
double 에서 어떻게 그리고 유사하게 작동 하는지에 대한 구현 세부 사항 일뿐입니다 . 그들은 부호 비트를보고 "-"가 인쇄되어야하는지 여부를 결정합니다 (nan 여부에 관계없이). 그래서 당신이 맞습니다. "nan"과 "-nan"사이에는 의미있는 차이가 없습니다.이 부분은 곧 수정됩니다.
파이썬의 재미있는 정의. 우리는 펜과 종이와 함께이 문제를 해결하는 경우 그 예상 결과가 될 말을 expected: (inf + 0j)
우리가 우리의 규범 의미하는 것을 알고 있기 때문에 당신이 지적했듯이 1
정도 (float('inf')+0j)*1 =should= ('inf'+0j)
:
그러나 그것은 당신이 볼 수있는 경우가 아닙니다. 우리가 그것을 실행하면 우리는 다음을 얻습니다 :
>>> Complex( float('inf') , 0j ) * 1
result: (inf + nanj)
파이썬이 이해 *1
복소수와 아닌 규범으로 1
이 같은 해석, 그래서 *(1+0j)
우리가해야 할 때 오류가 나타납니다 inf * 0j = nanj
으로 inf*0
확인할 수 없습니다.
실제로 원하는 작업 (1이 1의 표준이라고 가정) :
만약 리콜 z = x + iy
실수 부와 허수 부 (X)의 Y의 복소 공액 복소수 인 z
것으로 정의되는 z* = x − iy
, 상기 절대 값 역시 호출 norm of z
로 정의한다 :
가정 1
은 1
우리가 다음과 같이해야하는 표준 이라고 가정 합니다 .
>>> c_num = complex(float('inf'),0)
>>> value = 1
>>> realPart=(c_num.real)*value
>>> imagPart=(c_num.imag)*value
>>> complex(realPart,imagPart)
result: (inf+0j)
별로 직관적 인 건 아니지만 ... 때때로 코딩 언어는 우리가 일상에서 사용하는 방식과 다른 방식으로 정의됩니다.