정확한 우측 시프트 오버플로
정확한 우측 시프트 오버플로
@shrExact은 이름 그대로 정확한(exact) 시프트를 수행해요. 즉 시프트하면서 값에서 밀려나가 버려지는 비트가 하나라도 있다면 그걸 오버플로로 취급해요. 그래서 컴파일 타임에는 오류가, 런타임에는 패닉이 발생하죠. 정확한 시프트가 왜 위험한지, 그리고 어떤 식으로 잡히는지 실제 예제를 보면서 확인해 볼게요.
본문
컴파일 타임에 오버플로가 생기는 경우부터 볼게요.
// test_comptime_shrExact_overflow.zig
comptime {
const x = @shrExact(@as(u8, 0b10101010), 2);
_ = x;
}
$ zig test test_comptime_shrExact_overflow.zig
error: exact shift shifted out 1 bits
const x = @shrExact(@as(u8, 0b10101010), 2);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0b10101010을 오른쪽으로 2비트 밀면 0b00101010이 되는데, 이때 아랫 2비트 10이 밖으로 나가 버려요. 정확한 시프트는 이런 비트 손실을 오류로 봐서, 컴파일러가 exact shift shifted out 1 bits라는 오류를 내주는 거예요.
이번에는 런타임에서 같은 상황을 만들어 볼게요.
// runtime_shrExact_overflow.zig
const builtin = @import("builtin");
const std = @import("std");
pub fn main() void {
var x: u8 = 0b10101010; // runtime-known
_ = &x;
const y = @shrExact(x, 2);
std.debug.print("value: {}\n", .{y});
if ((builtin.cpu.arch.isPowerPC() or builtin.cpu.arch.isRISCV() or builtin.cpu.arch.isLoongArch() or builtin.cpu.arch == .s390x) and builtin.zig_backend == .stage2_llvm) @panic("https://github.com/ziglang/zig/issues/24304");
}
$ zig build-exe runtime_shrExact_overflow.zig
$ ./runtime_shrExact_overflow
thread 975074 panic: right shift overflowed bits
여기서 x는 // runtime-known 주석대로 런타임에야 값이 정해지는 변수예요. 값이 컴파일 타임에 알려져 있지 않으니 미리 오류를 낼 수 없고, 실제로 실행되는 순간 right shift overflowed bits라는 패닉과 함께 프로세스가 죽게 돼요. 값이 밀리면서 비트가 손실된다는 점은 같지만, 컴파일 타임에는 잡을 수 없어서 런타임까지 온 경우죠.
더 알아보기
@shrExact같은 정확한 연산은 컴파일 타임에는 오류로, 런타임에서는 패닉으로 이어져요. 같은 방식으로 동작하는 나머지 오버플로 사례(예: Division by Zero)도 함께 보면 그림이 더 선명해져요.- 시프트하면서 버려지는 비트가 애초에 없다면 오버플로가 나지 않아요. 이 섹션은 정확한 시프트(
@shrExact)가 비트 손실을 어떻게 잡는지를 다룬 거라, 일반 우측 시프트(>>)와의 차이를 기억해 두면 좋아요.