수치 연산
정수 wrap, checked 연산, 시프트, 형변환 오류와 부동소수점 결과를 설명합니다.
Wave Foundation
정수 표현
N비트 정수는 N개의 값 비트를 갖습니다. 부호 없는 정수의 범위는 0부터 2^N − 1까지이고, 부호 있는 정수의 범위는 −2^(N−1)부터 2^(N−1) − 1까지입니다. 연산의 signedness가 비트열의 해석을 결정합니다.
아래 예제는 연산 결과를 설명하며 텍스트 IR 문법을 나타내지 않습니다.
덧셈·뺄셈·곱셈
기본 정수 add·sub·mul은 결과의 하위 N비트를 유지합니다. overflow는 trap을 발생시키지 않습니다. checked 연산은 같은 wrap 결과와 함께, 수학적 결과가 해당 signed 또는 unsigned 범위를 벗어났는지를 나타내는 Bool을 반환합니다.
| 연산 | Wrap 결과 | Checked overflow |
|---|---|---|
| u8: 255 + 1 | 0 | true |
| i8: 127 + 1 | −128 | true |
| u8: 0 − 1 | 255 | true |
| i8: 12 × 3 | 36 | false |
overflow에서 실행을 중단하는 언어의 프런트엔드는 checked 연산의 overflow 결과에 명시적인 trap_if를 사용해야 합니다. 기본 연산이 소스 언어의 overflow 정책을 암묵적으로 적용하지는 않습니다.
나눗셈과 나머지
정수의 0 나눗셈과 0에 대한 나머지는 trap입니다. signed 최솟값을 −1로 나누면 최솟값으로 wrap합니다. 이 경우의 나머지는 0입니다.
| 연산 | 결과 |
|---|---|
| i8: −128 / −1 | −128 |
| i8: −128 % −1 | 0 |
| 정수 0 나눗셈 | trap |
| 정수 0에 대한 나머지 | trap |
시프트
N비트 값의 시프트에서는 count의 비트열을 unsigned로 해석한 뒤 N으로 나눈 나머지를 사용합니다. count가 0부터 N−1까지의 범위 밖이라는 이유만으로 trap을 발생시키지 않습니다.
8비트 값에서 count 0·8·16은 모두 0비트 시프트입니다. 8비트 count의 비트열 11111111은 7비트 시프트가 됩니다. 이 비트열이 signed −1을 나타내더라도 같습니다. unsigned 해석은 나머지 계산보다 먼저 이루어집니다.
음수나 과도한 count를 거부하는 소스 언어는 시프트 앞에 명시적인 검사를 넣어야 합니다.
형변환
| 변환 | 의미 |
|---|---|
| Zero extension | 상위 비트를 0으로 채워 폭을 늘림 |
| Sign extension | 부호 비트를 복제해 폭을 늘림 |
| 비트 절단 | 목적지 폭에 해당하는 하위 비트만 유지 |
| 비트 재해석 | 같은 비트열을 다른 타입으로 해석 |
| 손실 없는 수치 변환 | 수치를 보존하며 표현할 수 없으면 trap |
예를 들어 8비트 11111111을 16비트로 zero extension하면 0000000011111111, sign extension하면 1111111111111111입니다. 입력 비트가 같아도 서로 다른 연산입니다.
float→int는 0 방향으로 절삭한 뒤 정수 범위를 검사합니다. NaN과 무한대는 trap입니다. i8로 변환할 때 127.9는 127이 되고, 128.0은 trap입니다. Bool은 정수 0 또는 1로 변환합니다. signed i1은 1을 표현할 수 없으므로 이 변환의 목적지로 사용할 수 없습니다.
주소를 정수로 변환해도 그 정수로 포인터 접근 권한을 복구할 수 있는 것은 아닙니다. 포인터 유효성을 참고하세요.
부동소수점 연산
부동소수점 값은 정확한 f16·f32·f64 비트열을 갖습니다. 연산은 선언된 폭에서 가장 가까운 값으로 반올림하고, 정확히 중간인 경우 유효숫자의 최하위 비트가 짝수인 값을 선택하는 ties-to-even을 사용합니다.
기본 연산은 fast-math, 암묵적 FMA, 작은 값의 강제 0 처리를 허용하지 않습니다. 곱셈 다음 덧셈은 각각의 반올림 단계를 유지하며 백엔드가 암묵적으로 하나의 연산으로 합쳐서는 안 됩니다.
수치 연산의 결과는 NaN이나 무한대일 수 있습니다. 수치 연산과 폭 변경에서 나오는 NaN은 폭별로 하나의 고정된 양의 quiet NaN으로 정규화합니다. 저장과 복사는 원래 NaN 비트를 보존합니다. 따라서 NaN payload를 산술 연산 없이 메모리로 전달하는 경우와 계산하는 경우의 동작이 다릅니다.
부동소수점 상태 플래그는 노출하지 않습니다. 기본 부동소수점 산술이 NaN·무한대 결과를 허용하더라도 float→int 변환에는 위의 trap 규칙을 적용합니다.