(inf + 0j) * 1이 inf + nanj로 평가되는 이유는 무엇입니까?


97
>>> (float('inf')+0j)*1
(inf+nanj)

왜? 이것은 내 코드에서 불쾌한 버그를 일으켰습니다.

1곱셈 적 정체성 은 왜 주지 (inf + 0j)않습니까?


1
찾고 계신 키워드가 " field "인 것 같습니다. 덧셈과 곱셈은 기본적으로 단일 필드 내에서 정의되며,이 경우 코드를 수용 할 수있는 유일한 표준 필드는 복소수 필드이므로 연산이 제대로 수행되기 전에 두 숫자 모두 기본적으로 복소수로 처리되어야합니다. 한정된. 그것은 그들이 이러한 정의를 확장 할 수 없다고 말하는 것이 아니라 , 분명히 그들은 표준적인 것을 사용했고 정의를 확장하려는 충동을 느끼지 않았습니다.
user541686

1
아, 그리고 이러한 특이한 점이 실망스럽고 컴퓨터를 때리고 싶다면 내 동정심을 가지고 있습니다 .
user541686

2
유한 요소가 아닌 요소를 추가하면 @Mehrdad는 필드가되지 않습니다. 실제로 곱셈 중립이 더 이상 없기 때문에 정의상 필드가 될 수 없습니다.
폴 기갑

@PaulPanzer : 네, 나중에 그 요소들을 밀어 넣은 것 같아요.
user541686 19-09-22

1
부동 소수점 숫자 (무한대 및 NaN을 제외하더라도)는 필드가 아닙니다. 필드를 유지하는 대부분의 ID는 부동 소수점 숫자를 유지하지 않습니다.
플러그 워시

답변:


95

1먼저 복소수로 변환되고 1 + 0j, 이는 내지 An 후 리드 inf * 0A의 결과 곱셈 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

8
"왜 ...?"라는 질문에 답하기 위해 가장 중요한 단계는 첫 번째 단계 인 where 1is cast to 1 + 0j.
Warren Weckesser 19 년

5
C99 는 복합 유형 (초안 표준의 섹션 6.3.1.8)을 곱할 때 실제 부동 소수점 유형이 복합으로 승격 되지 않도록 지정하며 , 내가 아는 한 C ++의 std :: complex도 마찬가지입니다. 이는 부분적으로 성능상의 이유 일 수 있지만 불필요한 NaN도 방지합니다.
benrg

@benrg NumPy array([inf+0j])*1에서도 array([inf+nanj]). 실제 곱셈이 C / C ++ 코드 어딘가에서 발생한다고 가정하면 _Complex 또는 std :: complex를 사용하는 대신 CPython 동작을 에뮬레이트하기 위해 사용자 지정 코드를 작성했음을 의미합니까?
marnix

1
@marnix 그것보다 더 관여합니다. 거의 모든 연산자와 함수가 파생되는 numpy하나의 중앙 클래스가 ufunc있습니다. ufunc방송 관리는 어레이 작업을 매우 편리하게 만드는 까다로운 관리자를 모두 처리합니다. 보다 정확하게는 특정 연산자와 일반 기계 간의 노동 분할은 특정 연산자가 처리하려는 입력 및 출력 요소 유형의 각 조합에 대해 "가장 안쪽 루프"세트를 구현한다는 것입니다. 일반 기계는 ... 어떤 외부 루프를 돌봐 및 루프 안쪽에 최적의 일치를 선택
폴 기갑

1
... 필요에 따라 정확히 일치하지 않는 유형을 홍보합니다. 이 수율에 대한 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"
Paul Panzer

32

기계적으로 받아 들여진 대답은 물론 옳지 만 더 깊은 대답이 주어질 수 있다고 주장합니다.

첫째, @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.

그 부록 G 것

이제 단순하게 유지하기 위해 복잡한 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는 작동 방식 infnan상호 작용 에 대한 규칙을 변경하는 것을 포함하여 최선을 다하고 있습니다 (본질적으로 inf우선 nan). OP의 문제는 실수와 제안 된 순수 상상의 유형을 복합으로 승격시키지 않음으로써 회피되지만 실제 1이 복합 1과 다르게 행동하는 것은 해결책으로 생각되지 않습니다. 분명히 Annex G는 두 무한대의 곱이 무엇인지 완전히 지정하지 못합니다.

더 잘할 수 있습니까?

더 나은 무한 기하학을 선택하여 이러한 문제를 해결하려는 유혹이 있습니다. 확장 된 실제 선과 유사하게 각 방향에 대해 하나의 무한대를 추가 할 수 있습니다. 이 구조는 투영 평면과 비슷하지만 반대 방향으로 함께 뭉치지는 않습니다. 무한대는 극좌표 inf xe ^ {2 omega pi i}로 표현되며, 제품을 정의하는 것은 간단합니다. 특히 OP의 문제는 자연스럽게 해결 될 것입니다.

그러나 이것이 좋은 소식이 끝나는 곳입니다. 어떤면에서 우리는 우리의 새로운 스타일 무한대가 실제 또는 가상 부분을 추출하는 기능을 지원하도록 요구함으로써 정점으로 돌아갈 수 있습니다. 추가는 또 다른 문제입니다. 두 개의 nonantipodal 무한대를 추가하면 각도를 정의되지 않음으로 설정 nan해야합니다. 즉 , 각도가 두 입력 각도 사이에 있어야한다고 주장 할 수 있지만 "부분적 난감"을 나타내는 간단한 방법은 없습니다.

구조에 Riemann

이 모든 점을 고려할 때 좋은 오래된 1 점 압축이 가장 안전한 방법 일 수 있습니다. 아마도 Annex G의 저자는 cproj모든 무한대를 하나로 묶는 기능 을 명령 할 때 똑같이 느꼈을 것입니다.


여기에 저 보다 주제에 대해 더 유능한 사람들이 대답 한 관련 질문이 있습니다.


5
예, 왜냐하면 nan != nan. 이 답변이 반 농담이라는 것을 이해하지만 작성 된 방식으로 OP에 도움이되어야하는 이유를 알지 못합니다.
cmaster-monica reinstate

질문 본문의 코드가 실제로 사용하지 않았고 ==다른 답변을 수락 한 경우 OP가 제목을 어떻게 표현했는지에 대한 문제인 것 같습니다. 나는 그 불일치를 수정하기 위해 제목을 변경했습니다. (저는 @cmaster에 동의하기 때문에이 답변의 전반부를 의도적으로 무효화합니다.이 질문이 묻는 것이 아닙니다).
Peter Cordes

3
극좌표를 사용하면 복잡한 곱셈을 배율 및 회전으로 볼 수 있기 때문에 문제가 될 @PeterCordes입니다. 1을 곱하는 경우처럼 무한한 "팔"을 0도 회전 시키면 끝이 유한 한 정밀도로 배치 될 것으로 기대할 수 없습니다. 이것은 제 생각에 받아 들여진 것보다 더 깊은 설명이고 또한 nan! = nan 규칙에 에코가있는 설명입니다.
폴 기갑

3
C99는 복합 유형 (초안 표준의 섹션 6.3.1.8)을 곱할 때 실제 부동 소수점 유형이 복합으로 승격되지 않도록 지정하며, 내가 아는 한 C ++의 std :: complex도 마찬가지입니다. 이는 1이 해당 언어의 해당 유형에 대한 곱셈 식별임을 의미합니다. 파이썬도 똑같이해야합니다. 현재 동작은 단순히 버그라고 부를 것입니다.
benrg

2
@PaulPanzer : 그렇지는 않지만 기본 개념은 0 (Z라고 부를 것임)이 항상 x + Z = x 및 x * Z = Z를 유지하고 1 / Z = NaN, 1 (긍정적 무한소)는 1 / P = + INF를 유지하고, 하나 (음의 무한소)는 1 / N = -INF를 유지하고, (부호없는 무한소)는 1 / U = NaN을 산출합니다. 일반적으로 x가 진정한 정수가 아닌 경우 xx는 U입니다.이 경우 Z를 산출합니다.
supercat

6

이것은 CPython에서 복잡한 곱셈이 구현되는 방법에 대한 구현 세부 사항입니다. 다른 언어 (예 : C 또는 C ++)와 달리 CPython은 다소 단순한 접근 방식을 취합니다.

  1. int / floats는 곱셈에서 복소수로 승격됩니다.
  2. 무한한 숫자가 관련 되 자마자 원하는 / 예상 된 결과를 제공하지 않는 간단한 학교 공식이 사용됩니다 .
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으로 승격되지 complexC99에 곱한이다 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을위한 등)이 여기에 역할을하지 않습니다 )도 그들 중 일부는 부호 비트가 설정되어 (따라서로 인쇄, "-"볼 ) 일부는 아닙니다.

적어도 반 직관적입니다.


여기서 중요한 점은 "단순한"복소수 곱셈 (또는 나눗셈)에 대한 단순한 것이 없으며 언어 나 컴파일러간에 전환 할 때 미묘한 버그 / 차이에 대비해야한다는 것입니다.


난 비트 패턴이 많이 있다는 것을 알고 있습니다. 그래도 사인 비트는 몰랐습니다. 하지만 의미 론적으로 -nan은 nan과 어떻게 다릅니 까? 아니면 난이 난과 더 다르다고 말해야할까요?
폴 기갑

@PaulPanzer 이것은 printfdouble 에서 어떻게 그리고 유사하게 작동 하는지에 대한 구현 세부 사항 일뿐입니다 . 그들은 부호 비트를보고 "-"가 인쇄되어야하는지 여부를 결정합니다 (nan 여부에 관계없이). 그래서 당신이 맞습니다. "nan"과 "-nan"사이에는 의미있는 차이가 없습니다.이 부분은 곧 수정됩니다.
ead

오 굿. 모든 것을 내가 ... 내가 약 FP 실제로 정확하지 알고 있었다 생각하는 미주리에 대한 걱정되었습니다
폴 기갑

성가 시게해서 미안하지만 "가상 1.0은 존재하지 않는다. 즉, 곱셈과 관련하여 0.0 + 1.0j와 같지 않은 1.0j"라고 확신 하는가? 맞다? 부속서 G는 어떻게해야 처방도 순수 가상의 유형 (G.2) 등을 지정하는 것 곱한 등 (G.5.1)
폴 기갑

@PaulPanzer 아니요, 문제를 지적 해 주셔서 감사합니다! C ++ 코더로서 저는 대부분 C ++ 글래스를 통해 C99 표준을 봅니다. C가 여기에서 한발 앞서 있다는 사실이 제 생각을 미쳤습니다. 분명히 다시 한 번 말입니다.
ead

3

파이썬의 재미있는 정의. 우리는 펜과 종이와 함께이 문제를 해결하는 경우 그 예상 결과가 될 말을 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로 정의한다 :

여기에 이미지 설명 입력

가정 11우리가 다음과 같이해야하는 표준 이라고 가정 합니다 .

>>> c_num = complex(float('inf'),0)
>>> value = 1
>>> realPart=(c_num.real)*value
>>> imagPart=(c_num.imag)*value
>>> complex(realPart,imagPart)
result: (inf+0j)

별로 직관적 인 건 아니지만 ... 때때로 코딩 언어는 우리가 일상에서 사용하는 방식과 다른 방식으로 정의됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.