왜 정수 0으로 나누면 패닉이 떨어질까?
go
package main
import (
"fmt"
)
func main() {
ls := []int{3, 0}
va := ls[0] / ls[1]
fmt.Println(va)
}위와 같은 코드를 실행하게 되면
bash
go run main.go
panic: runtime error: integer divide by zero
goroutine 1 [running]:
main.main()
/Users/pjt/Projects/PJT/tmp/go/ps/main.go:9 +0x1c
exit status 2이와 같은 패닉이 발생한다.
하지만 ls := []float32{3.0, 0.0} 정수가 아닌 소수를 사용하면 패닉이 발생하지 않음
bash
go run main.go
+Inf정수 나누기 ALU 부터
정수의 나누기는 CPU의 ALU에서 처리하게 된다.
DIV,IDIV(부호가 없는 나누기와 , 부호가 있는 나누기)
ALU가 하는 역할은 입력이 오면 연산을 하고 출력을 한다, 그런데 0으로 나누는 것은 몫이 수학적으로 정의되지 않기도 하고
정수형 메모리 구조에서는 무한대를 표현할 수 있는 비트패턴이 존재하지 않는다.
따라서 CPU는 하드웨어 예외를 발생시킴 (x86의 Divide Error Exception 처럼)
여기서 하드웨어의 예외가 발생하게 되면 제어권이 OS 커널로 이동하게 된다.
리눅스 같은 OS는 이 예외를 해당 프로세스의 SIGFPE (Floating-Point Exception) 시그널로 전달한다
이후 Go의 런타임에서 해당 시그널을 받아 Go패닉을 발생시킨다.
부동소수점은 왜 패닉이 발생하지 않는가?
처음 실행 결과를 보면 소수를 사용했을 때는 프로그램이 패닉을 발생하지 않고 정상적으로 결과를 출력하였다.
소수 계산은 ALU가 처리하는 것이 아닌 부동소수점 전용 연산장치인 FPU가 담당한다. (Floating Point Unit)
이때 IEEE 754 라는 표준을 따르게 되는데 해당 표준에서는 "0으로 나누면 어떻게 할지" 를 명시적으로 정의해놓음.
text
float32 비트 구조 (32비트):
[1비트 부호] [8비트 지수] [23비트 가수]
지수부가 전부 1 (0xFF) 이면 → 특수값
가수부가 0 → ±Inf
가수부가 0 아님 → NaN
+Inf: 0 11111111 00000000000000000000000
-Inf: 1 11111111 00000000000000000000000
NaN: _ 11111111 00000000000000000000001 (가수부 아무거나 0 아니면)+Inf, -Inf, NaN은 IEEE 754가 비트 패턴으로 정의한 실제 값이다.
text
3.0 / 0.0
→ +Inf (양수 ÷ +0)
-3.0 / 0.0
→ -Inf (음수 ÷ +0)
0.0 / 0.0
→ NaN (정의 불가)이렇게 FPU 는 ALU와 달리 exception 던지는 대신 이 비트 패턴을 결과 레지스터에 쓰고 끝낸다