[CS] "나누기 0"은 왜 어떤 언어에선 에러가 나고, 어떤 언어에선 무한대(Infinity)가 될까?

2025. 11. 3. 17:38·Development
자바 예외처리(exception)에 대해 보다가 문득 ArithmeticException에 대해 궁금해져서 정리하게 된 글입니다.

 

개요

아래 코드의 실행 결과는 뭘까?

public class Main {

    public static void main(String[] args) {

        int a = 5, b = 0;

        try {
            System.out.print(a / b);
        } catch (ArithmeticException e) {
            System.out.print("출력1");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.print("출력2");
        } catch (NumberFormatException e) {
            System.out.print("출력3");
        } catch (Exception e) {
            System.out.print("출력4");
        } finally {
            System.out.print("출력5");
        }
    }
}

정답은 '출력1출력5' 이다.

첫번째 try문에서 a/b를 할 때, 0으로 나누기 때문에, `ArithmeticException`이 발생하기 때문이다.

 

개발자라면 한 번쯤 "0으로 나누기" 에러를 본 적이 있을 것이다. 자바(Java)에서는 `ArithmeticException`이 발생하는데, 자바스크립트(JavaScript)에서는 `Infinity`라는 값이 튀어나온다.

JavaScript실행결과

왜 이런 차이가 발생하는 걸까? 단순히 "언어마다 다르다"라고 외우는 것을 넘어, 컴퓨터가 수학적으로 불가능한 연산을 어떻게 처리하는지에 대한 '동작 원리'로 이해해보자.

 


1. 근본적인 문제: CPU는 0으로 나누는 것을 어떻게 처리할까?

모든 프로그래밍 언어는 결국 CPU(중앙 처리 장치)에서 기계어로 번역되어 실행된다. 우리가 5 / 0 코드를 작성하면, CPU는 이 연산을 수행해야 한다.

이때, CPU는 연산하려는 숫자의 타입에 따라 두 가지 다른 방식으로 동작한다.

 

가. 정수 (int, long) 연산: "불가능한 명령"

  • CPU 동작: 정수 연산 장치(ALU)는 5 / 0 (정수 나누기)을 "수학적으로 정의되지 않아 실행 불가능한" 명령으로 인식한다.
  • 결과: CPU는 즉시 하던 일을 멈추고, 운영체제(OS)에 "치명적인 오류가 발생했다!"는 하드웨어 인터럽트(트랩) 신호를 보낸다. (정확히는 Divide Error 신호)

 

나. 실수 (float, double) 연산: "특별한 값으로 처리"

  • CPU 동작: 실수(부동소수점) 연산 장치(FPU)는 IEEE 754라는 국제 표준을 따른다. 이 표준에는 "무한대(Infinity)"와 "숫자가 아님(NaN, Not a Number)"이라는 특별한 값이 정의되어 있다.
  • 결과: 하드웨어 오류가 발생하지 않는다. 대신 표준에 따라 5.0 / 0.0은 Infinity로, 0.0 / 0.0은 NaN으로 처리된 '유효한' 값을 반환한다.

 


2. 언어들의 3가지 선택: 안전성 vs 유연성 vs 성능

CPU가 이렇게 두 가지 방식으로 동작하기 때문에, 각 프로그래밍 언어는 이 신호(혹은 값)를 어떻게 처리할지 '설계 철학'에 따라 선택해야 했다.

 

철학 1: 안전성 우선 (Exceptions) - "개발자에게 오류를 명확히 알려준다"

"정수 0 나눗셈은 하드웨어 오류야. 이걸 무시하고 넘어가면 프로그램에 더 큰 버그가 생길 수 있어. 즉시 예외(Exception)를 발생시켜서 개발자가 이 문제를 인지하고 처리하도록 강제하자."

  • 언어: Java, Python, C#
  • 동작: CPU의 하드웨어 인터럽트 신호를 받아서, 각 언어의 문법에 맞는 예외 객체로 감싸서(Wrapping) 던진다.
    • Java: ArithmeticException
    • Python: ZeroDivisionError
    • C#: DivideByZeroException
  • 특징: 실수(float, double) 연산은 IEEE 754 표준을 따라 Infinity를 반환하므로, 정수와 실수의 동작이 다르다.
// Java
System.out.println(5 / 0);      // 🛑 ArithmeticException 발생
System.out.println(5.0 / 0.0);  // Infinity (예외 아님)

 

철학 2: 유연성 우선 (Special Values) - "어떻게든 계산을 이어나간다"

"프로그램이 멈추는(crashing) 것이 최악이다. 특히 웹 브라우저 같은 환경에서는 계산 하나 잘못했다고 전체 스크립트가 멈추면 안 된다. 모든 숫자를 실수처럼 취급해서 '무한대'라는 값으로라도 계산을 이어가자."

  • 언어: JavaScript
  • 동작: JavaScript는 Java와 달리 int나 long 같은 순수 정수 타입이 없다. 모든 숫자는 내부적으로 64비트 부동소수점(Java의 double과 유사)으로 처리된다.
  • 특징: 5 / 0이든 5.0 / 0.0이든 모두 실수 연산으로 취급되어 IEEE 754 표준에 따라 Infinity를 반환한다. 예외가 발생하지 않는다.
// JavaScript
5 / 0     // Infinity (예외 아님)
-5 / 0    // -Infinity
0 / 0     // NaN

 

철학 3: 성능 우선 (Undefined Behavior) - "하드웨어에 모든 것을 맡긴다"

"언어 차원에서 이런 예외 처리를 추가하는 것은 모두 약간의 성능 저하를 유발한다. 우리는 1클럭이라도 빨라야 한다. 하드웨어의 오류 신호를 그대로 노출시키고, 그 책임은 프로그래머에게 맡긴다."

  • 언어: C, C++
  • 동작 (정수): 정수 0 나눗셈에 대한 하드웨어 오류를 언어 차원에서 막아주지 않는다. 이는 **"정의되지 않은 동작 (Undefined Behavior, UB)"**을 유발한다.
  • 결과 (UB): 프로그램이 그 즉시 충돌(crash)할 수도, 이상한 쓰레기 값을 반환할 수도, 혹은 당장은 멀쩡해 보이다가 나중에 완전히 다른 곳에서 오작동할 수도 있다. 무슨 일이 일어날지 아무도 보장할 수 없어 가장 위험하다.

 


3. 한눈에 비교하기

언어 정수 / 0 (e.g., 5 / 0) 실수 / 0.0 (e.g., 5.0 / 0.0) 설계 철학
Java 🛑 ArithmeticException (예외) Infinity (무한대 값) 안전성 (타입별 분리)
Python 🛑 ZeroDivisionError (예외) 🛑 ZeroDivisionError (예외)¹ 안전성 (매우 엄격)
C# 🛑 DivideByZeroException (예외) Infinity (무한대 값) 안전성 (Java와 동일)
JavaScript Infinity (무한대 값)² Infinity (무한대 값) 유연성 (모두 실수 취급)
C / C++ 💥 Undefined Behavior (UB) Infinity (표준에 따름) 성능 (하드웨어 직결)

¹ Python은 실수 연산조차 0으로 나누는 것을 허용하지 않고 예외를 발생시키는 매우 엄격한 정책을 사용한다.

² JavaScript는 int 타입이 없으므로 5 / 0 자체가 부동소수점 연산이 된다.

 


결론

"0으로 나누기"라는 단순해 보이는 연산 하나에도 이처럼 복잡한 하드웨어의 동작 원리와, 각 언어가 추구하는 안전성, 유연성, 성능이라는 핵심 가치가 담겨 있다.

내가 사용하는 언어가 왜 이런 선택을 했는지 이해한다면, 예외를 만나도 당황하지 않고 더욱 견고한 코드를 작성하는 데 도움이 될 것이다.

'Development' 카테고리의 다른 글

권한 관리, 언제까지 if-else로 땜빵할 거야? (RBAC부터 Zanzibar 찍먹까지)  (1) 2025.12.08
[Cursor] Playwright로 테스트부터 디버깅까지 한번에 끝내기  (0) 2025.11.11
[React] 배포 후 간헐적으로 개발 서버에서 화면 에러가 뜨는 이유  (0) 2025.10.14
[Web] 브라우저 “탭( Tab )”을 노리는 공격, Tabnabbing  (0) 2025.10.13
[C언어] return 0, 그리고 0과 1의 진짜 의미  (0) 2025.10.06
'Development' 카테고리의 다른 글
  • 권한 관리, 언제까지 if-else로 땜빵할 거야? (RBAC부터 Zanzibar 찍먹까지)
  • [Cursor] Playwright로 테스트부터 디버깅까지 한번에 끝내기
  • [React] 배포 후 간헐적으로 개발 서버에서 화면 에러가 뜨는 이유
  • [Web] 브라우저 “탭( Tab )”을 노리는 공격, Tabnabbing
곽진돔
곽진돔
Developer
  • 곽진돔
    echo "곽박한 세상";
    곽진돔
  • 전체
    오늘
    어제
    • 분류 전체보기 (203)
      • Development (74)
        • Linux (13)
        • k8s (3)
        • Docker (5)
        • AWS (1)
        • PHP (35)
        • Python (21)
        • Java (1)
        • SpringBoot (4)
        • JavaScript (2)
        • React (10)
        • MySql (19)
        • MongoDB (1)
      • Daily (6)
      • Study (7)
        • TIL (2)
        • license (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 글쓰기
    • 설정
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    스프링부트
    ssh
    chromedriver
    error
    nodejs
    Java
    docker
    SQL
    인코딩
    HTML
    정규표현식
    CentOS7
    Shell
    Linux
    리액트
    CentOS
    IP
    MySQL
    Selenium
    윈도우
    php
    크롤링
    springboot
    JavaScript
    Python
    react
    db
    date
    UTF8
    리눅스
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
곽진돔
[CS] "나누기 0"은 왜 어떤 언어에선 에러가 나고, 어떤 언어에선 무한대(Infinity)가 될까?
상단으로

티스토리툴바