기술 문서

Delphi 공학 함수: 진수 변환, 복소수 수학

Excel의 공학 함수 제품군은 함수 참조 중 가장 쉬운 모서리처럼 읽힙니다. DEC2BIN은 숫자를 이진 문자열로 바꿉니다. HEX2DEC는 그것을 다시 돌려놓습니다. IMSUM은 두 복소수를 더합니다. 각각은 서식을 지정하는 연습처럼 보입니다. 하지만 그렇지 않습니다. 이러한 이름 뒤에는 대부분의 개발자가 컴퓨터 아키텍처 수업 이후 만져본 적 없는 10비트 2의 보수 인코딩, 전적으로 문자열 안에 있는 복소수 형식, 확인하기 전에 이동하면 64비트 정수를 조용히 오버플로시킬 비트 연산자가 있습니다. Excel을 정확히 재현하는 스프레드시트 엔진은 이들 중 어느 것도 대충 넘길 수 없습니다

이 기능들은 세 개의 그룹으로 나뉘며, 각 그룹은 다른 트랩을 숨기고 있습니다. 진수 변환은 음수와 진수 당 임계값에 관한 것입니다. 복소수 산술은 문자열을 구문 분석하고 포맷하는 것에 관한 것입니다. 비트 연산은 Int64의 경계 안에 머무는 것에 관한 것입니다. 이 기사에서는 당신이 실제로 쓸 워크시트 호출과 함께, HotXLS가 어떻게 각 그룹을 구현하는지 살펴봅니다

진수 변환 및 10비트 2의 보수

정방향 방향은 모든 사람들이 예상하는 부분입니다. DEC2BIN(9)"1001"을 부여하며 선택적인 두 번째 인수는 결과를 고정 폭으로 왼쪽에 패딩합니다. 트랩은 음수 입력입니다. Excel은 마이너스 기호를 쓰지 않습니다. 값을 대상 진수의 10자리 2의 보수 문자열로 인코딩하며, 이것이 DEC2BIN(-5,10)이 기호가 있는 어떤 것이 아니라 "1111111011"을 반환하는 이유입니다. 인코딩이 이미 10자리로 고정되어 있기 때문에 값이 음수가 되면 자릿수 인수는 무시됩니다

10자리는 고정된 예산이며 이 예산이 진수별로 표현할 수 있는 범위를 설정합니다. 이진수에서 음의 반쪽으로 넘어가는 크기는 512이고 래핑 모듈러스는 1024이므로, 이진 문자열은 정확히 10자 길이고 그 값이 최소 512일 때만 서명됩니다. 이 동일한 아이디어가 진수별로 크기가 정해집니다. 팔진수는 절반의 임계값으로 2^29를, 전체 모듈러스로 2^30을 사용합니다. 십육진수는 2^39와 2^40을 사용합니다. HotXLS 리더는 이 규칙을 정확하게 적용합니다. 숫자들을 누적하고, 문자열이 폭 10문자이고 누적된 값이 절반 임계값에 도달하거나 초과할 때만 전체 모듈러스를 빼서 부호 있는 값을 복원합니다. 9문자 문자열은 아무리 크더라도 항상 양수입니다

인코더는 거울 이미지입니다. 음이 아닌 값은 숫자 단위로 변환되고 요청된 폭으로 선택적으로 영(zero) 패딩되며, 진수의 양의 상한을 넘쳐 흐르거나 요청된 폭이 이를 보관하기에 너무 좁은 경우 거부됩니다. 음의 값은 처음에 전체 모듈러스를 추가하여 범위로 가져옵니다. 이는 이를 밑 표현이 항상 열 자리인 값으로 바꾼 다음 자릿수를 배출할 때 선두의 0을 함께 배출하여 폭을 채웁니다. 밑별 단일 공유 범위 검사와 대칭적인 하한 및 상한 값 덕분에 DEC2BIN, DEC2OCT, DEC2HEX 함수가 경계값에서 서로 일관성을 유지할 수 있습니다

남은 것은 이름에 10진수를 거치지 않고 밑을 바꾸는 교차 밑 변환, 예를 들어 HEX2BINOCT2HEX입니다. 구현부는 모든 순서쌍에 대해 별도의 루틴을 가지고 있지 않습니다. 입력 문자열을 소스 기수를 사용하여 부호가 있는 십진 값으로 구문 분석한 다음, 해당 십진 값을 대상 기수로 서식을 지정합니다. 소수가 중심입니다. 1개의 파스 루틴과 1개의 포맷 루틴이 합쳐져 모든 조합을 처리하며, 양쪽 절반 모두 10자리 부호 있는 규칙을 공유하므로, 음수 값이라도 자신의 기호를 고스란히 지니고 무사히 넘어갈 수 있습니다

복소수는 문자열이므로 작업은 구문 분석입니다

Excel에는 복소수 데이터 타입이 없습니다. 복소수 값은 "a+bi" 문자열이고 IM 제품군의 모든 기능은 해당 문자열을 받아 하나를 되돌려줍니다. COMPLEX는 실수 및 허수 부분에서 문자열을 빌드합니다. IMSUM, IMSUB, IMPRODUCTIMDIV는 이들의 인수를 구문 분석하고 수치 부분에서 산술을 수행하고 그 결과를 다시 문자열로 서식을 지정합니다. 수치적인 작업은 학부 대수학입니다. 어려움은 텍스트를 두 개의 부동 소수점 숫자로 안정적으로 변환하는 것에 완전히 있고, 그것이 내부 파서가 제값을 하는 부분입니다

파서의 두 가지 세부 사항은 틀리기 쉽습니다. 첫 번째는 노출된 허수 단위입니다. "i" 문자열은 0이 아니고 오류가 아닌 1 곱하기 i를 의미하므로, 접미사 앞의 계수가 비어 있거나 단독 더하기 기호일 때 파서는 이를 값 1로 읽어야 하고 단독 빼기는 -1로 읽어야 합니다. 이를 건너뛰면 IMSUM("i","i")2i가 되기를 멈춥니다. 두 번째는 실수 부분과 허수 부분을 분리하는 부호와 충돌하는 과학적 표기법입니다. 파서는 더하기 또는 빼기를 스캔하여 해당 구분 기호를 찾지만 "1.5E-3"으로 쓰인 숫자에는 지수에 속하는 마이너스가 포함되어 있습니다. 따라서 파서는 바로 앞의 문자가 e이거나 E인 경우 덧셈과 뺄셈을 구분 기호로 처리하기를 거부합니다. 해당 가드가 없으면 지수 부호에서 실수 부분이 반으로 찢어지고 완벽하게 유효한 입력에 대해 파싱이 실패할 것입니다

접미사 자체는 정규화되지 않고 보존됩니다. Excel은 ij를 둘 다 수용하고 HotXLS는 포맷된 결과에 같은 글자가 수반되도록 입력이 어떤 것을 썼는지 기억합니다. 그런 다음 서식 지정에 일반적인 단축 기호가 적용됩니다. 1의 허수부는 접미사 자체로 인쇄되고, 빼기 1은 -i, 허수부가 0인 복소수는 순실수로 축소되고 0인 실수부는 맨 앞의 0+가 생략됩니다

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Engineering');
    // Negative input: a ten-bit two's complement, places argument ignored.
    Sheet.Cells[1, 1].Value := Sheet.Calculate('=DEC2BIN(-5,10)'); // 1111111011
    // Complex multiply on two "a+bi" strings.
    Sheet.Cells[2, 1].Value := Sheet.Calculate('=IMPRODUCT("3+4i","1+2i")'); // -5+10i
  finally
    Book.Free;
  end;
end;

초월 복소 함수, IMSQRT, IMEXP, IMLNIMPOWER 등은 직교 좌표계에서 동작하지 않습니다. 파싱된 값을 극좌표계의 형식으로 변환하고, 계수와 편각에 연산을 적용하고, 원래대로 바꿉니다. 제곱근은 편각을 반으로 나누고 계수의 근을 취합니다. 거듭제곱은 편각을 곱하고 계수를 올립니다. 그 어떤 다른 방식으로도 직교 좌표계에서 각 항등성을 재도출하는 것을 의미하며, 이는 코드가 더 많고 분지절단에 근접할수록 수치적으로 불안정합니다

비트 연산자와 가장 먼저 확인해야 하는 오버플로

Excel 2013에는 BITAND, BITOR, BITXOR, BITLSHIFTBITRSHIFT가 추가되었습니다. 피연산자는 제한됩니다: 각각은 2^48에서 1을 뺀 값 이하의 음이 아닌 정수이어야 하며, 분수 또는 음수 인수는 숫자 오류입니다. 해당 한도는 배정도에서 정확히 나타낼 수 있는 범위 내에 머물면서 어떤 현실적인 플래그 집합이라도 충분히 포괄할 정도로 관대합니다. Excel이 모든 숫자 인수를 부동 소수점 값으로 건네기 때문에 이 점이 중요합니다

시프트 함수에는 진정으로 문제의 소지가 있는 하나의 정렬 규칙이 수반됩니다. 왼쪽 시프트는 입력보다 훨씬 더 큰 값을 낼 수 있으며, shl을 먼저 수행하고 그 결과를 나중에 검사하면 이미 Int64 오버플로가 발생한 것이고 해당 테스트는 아무 의미가 없습니다. 확인은 시프트 이전에 와야 합니다. HotXLS는 시프트 양만큼 오른쪽으로 시프트한 천장과 피연산자를 비교하고, 그 피연산자가 맞을 때만 실제 왼쪽 시프트를 수행합니다. 53비트 이상의 시프트 크기는 바로 기각되고, 음수 시프트는 단순히 방향을 역전시키므로 음수 카운트가 있는 BITLSHIFT는 오른쪽 시프트로 행동합니다. 이 원리는 이 한 가지 기능을 훨씬 넘어서 일반화됩니다. 오버플로를 방지하기 위해 가드가 존재하는 경우 보호해야 할 결과가 아닌 입력에서 실행되어야 합니다

// Bitwise calls evaluate the same way through Calculate.
Sheet.Cells[3, 1].Value := Sheet.Calculate('=BITAND(13,11)');    // 9
Sheet.Cells[4, 1].Value := Sheet.Calculate('=BITLSHIFT(5,2)');   // 20
Sheet.Cells[5, 1].Value := Sheet.Calculate('=BITRSHIFT(40,3)');  // 5

미래 기능 및 _xlfn 이름 접두사

비트 단위의 조작 및 여타의 긴 2007년 이후 첨가 목록은 계산하는 바와 아무 관련이 없고 Excel이 해당 파일을 저장하는 방식과 연관이 아주 많은 작명 체계와 상호 작용합니다. 원본의 이진 워크시트 포맷은 고정된 표 내 숫자 슬롯에 각각의 내장 기능을 부여했습니다. 그 표가 동결된 후에 발명된 함수에는 슬롯이 없습니다. 이런 기능을 파일에 저장하고 근대의 엑셀에서 인식하게 하려면 _xlfn. 접미사를 덧붙여서 기록해야 합니다. 그리하여 BITAND_xlfn.BITAND로 보관되지만, 사용자가 항상 BITAND라고 타이핑할 따름입니다

함정은 규칙이 획일적이지 않다는 것입니다. 일부 최신 기능에는 테이블 슬롯이 주어지고 그대로 작성되는 반면, 소수의 레거시 숨겨진 기능 역시 그 오래된 나이에도 불구하고 접두사 없이 기록됩니다. HotXLS는 접두사를 필요로 하는 이름이 무엇인지 명시적인 화이트리스트를 보관하고 작성할 때는 추가하고 읽을 때는 이를 벗기므로, 귀하가 설정하고 다시 읽는 공식 텍스트는 언제나 깨끗한 엑셀 대면용 이름입니다. =BITLSHIFT(5,2)를 설정하면 파일에는 _xlfn.BITLSHIFT가 보관되고 값은 그럼에도 불구하고 20으로 되돌아옵니다. 접두사는 사용자가 코드에서 다루는 공식으로 새어나와서는 안 되는 스토리지 세부 사항입니다

워크시트에 함께 모으기

이 모든 것에 대한 공공 표면은 작습니다. TXLSXWorkbook을 생성하고, 워크시트를 추가하며, Cells[Row, Col].Formula를 통해 셀에 공식을 쓰고 다시 계산하거나, 워크시트의 Calculate 방법을 사용하여 표현식을 직접 평가합니다. 이는 해당 시트에 대한 공식을 컴파일하고 Variant를 돌려줍니다. 위의 예시에서 Calculate를 사용한 까닭은 에워싸는 시트 상태 없이 단일 엔지니어링 호출의 결과를 보여주기 때문인데, 통합 문서가 재계산할 때에 실질적인 셀 공식 내에서 동일한 함수가 같게 평가됩니다

인코딩은 염두에 두어야 할 부분이지, 콜 사이트가 아닙니다. 이진 문자열은 10자릿수에서 진수의 절반 임계값을 지난 경우에만 부호가 붙습니다. 복소수는 텍스트이고, 빈 허수 계수는 1이고 파서는 지수의 e를 넘어서 건너뜁니다. 왼쪽 시프트는 시프트 전에 검사됩니다. 이 네 가지 사실을 바로 잡으면 엔지니어링 제품군은 기호 오프(off-by-a-sign) 서프라이즈의 원천이 되지 않게 될 것입니다

동일한 엔진에 고유한 도메인 수학을 연결하는 경우 핸들러 등록과 값 반환의 역학은 사용자 지정 함수를 사용하여 수식 엔진을 확장하는 문서에서 다루고 있으며, 이러한 수식이 셀 주소가 아닌 이름으로 시트를 가로질러 접근해야 할 때, 정의된 이름과 시트 교차 수식에 대한 둘러보기에서 참조가 어떻게 해소되는지 보여줍니다. 여기에 설명된 엔지니어링 기능은 이 블로그의 다른 곳에서 다루는 읽기, 쓰기 및 계산 API와 함께 Delphi 및 C++Builder용 HotXLS 스프레드시트 컴포넌트의 일부로 출하됩니다