---
title: "signed 비교: 뺄셈은 맞았는데 작은 수가 더 크다고 나온다"
author: "브레이브브라우저쓰자"
published_at: "2026-10-11T09:19:01.426Z"
updated_at: "2026-10-06T00:03:22.195Z"
tags: ["signed 비교"]
canonical_url: "https://ariser.io/@writer_a9817d5a8de0/signed-%EB%B9%84%EA%B5%90-%EB%BA%84%EC%85%88%EC%9D%80-%EB%A7%9E%EC%95%98%EB%8A%94%EB%8D%B0-%EC%9E%91%EC%9D%80-%EC%88%98%EA%B0%80-%EB%8D%94-%ED%81%AC%EB%8B%A4%EA%B3%A0-%EB%82%98%EC%98%A8%EB%8B%A4"
ai_assisted: true
---

# signed 비교: 뺄셈은 맞았는데 작은 수가 더 크다고 나온다

이 글은 AI가 활용됐습니다.

[원문](https://ariser.io/@writer_a9817d5a8de0/signed-%EB%B9%84%EA%B5%90-%EB%BA%84%EC%85%88%EC%9D%80-%EB%A7%9E%EC%95%98%EB%8A%94%EB%8D%B0-%EC%9E%91%EC%9D%80-%EC%88%98%EA%B0%80-%EB%8D%94-%ED%81%AC%EB%8B%A4%EA%B3%A0-%EB%82%98%EC%98%A8%EB%8B%A4)

두 정수를 비교하려고 a−b를 계산한 뒤 최상위 비트를 읽었다. 음수면 a가 더 작다는 직관이다. 대부분의 값에서는 맞지만, signed 범위의 끝에서 정반대 답이 나온다. signed 비교에서 중요한 것은 뺄셈의 최상위 비트만이 아니라 **피연산자의 부호와 overflow가 그 비트의 해석을 바꾸는 조건**이다.

여기서는 8비트 2의 보수 signed 값, 즉 −128부터 127까지를 사용한다. 같은 비트열을 unsigned로 해석하는 비교와는 다른 함수다.

## −128이 1보다 크다고 나오는 순간

a=−128, b=1이면 a가 더 작다. 수학적인 차이는 −129다. 하지만 −129는 8비트 signed 범위를 벗어난다. 낮은 8비트만 남기면 결과는 01111111, 즉 +127이고 최상위 비트는 0이다.

`a−b의 MSB=1이면 a<b`라는 단순한 판정은 이 입력에서 거짓을 반환한다. 반대 방향도 가능하다. a=126, b=−3이면 실제로 a가 더 큰데 차이 129가 8비트 10000001, signed −127로 나타난다. 결과 비트가 음수처럼 보인다는 이유로 a가 작다고 판정하면 틀린다.

문제는 뺄셈기가 덧셈을 잘못했다는 것이 아니다. modulo 256의 낮은 결과 비트는 맞다. 그 비트를 범위 밖의 signed 차이 부호로 해석한 비교 논리가 잘못된 것이다.

## 부호가 다를 때는 빼지 않아도 답이 있다

signed 비교에서 a는 음수이고 b는 0 이상이면 a<b다. a가 0 이상이고 b가 음수이면 a<b는 거짓이다. 이 조건에서는 두 피연산자의 부호만으로 답을 정할 수 있다.

부호가 같으면 차이의 범위가 달라진다. 두 입력이 0–127이면 차이는 −127부터 127까지다. 두 입력이 −128–−1인 경우에도 차이는 같은 범위에 있다. 따라서 같은 부호의 두 8비트 값의 차이는 signed 범위에 들어가고, 뺄셈 결과의 MSB로 작은 쪽을 판단할 수 있다.

설명용 논리는 다음과 같다.

```systemverilog
logic [7:0] a_bits, b_bits;
logic [7:0] difference;
logic less_signed;

assign difference = a_bits - b_bits;
assign less_signed = (a_bits[7] != b_bits[7])
                   ? a_bits[7]
                   : difference[7];
```

입력은 signed 숫자를 나타내는 8비트 패턴이라는 계약이다. 코드의 비트 비교는 그 표현을 직접 사용한다. 변수 타입이 unsigned라고 해서 결과가 unsigned 비교인 것은 아니지만, 호출자가 입력을 어떤 숫자로 읽는지는 명확히 해야 한다.

## overflow 비트를 사용하는 같은 의미의 해법

뺄셈에서 signed overflow는 피연산자 부호가 다르고 결과 부호가 a와 다를 때 발생한다. `V = (sign_a XOR sign_b) AND (sign_result XOR sign_a)`로 표현할 수 있다.

정상적인 차이 부호를 얻는 less-than 조건은 `sign_result XOR V`다. −128−1에서는 result 부호 0, V=1이므로 less=1이다. 126−(−3)에서는 result 부호 1, V=1이므로 less=0이다. overflow가 감긴 결과 부호를 바로잡는다.

두 방식은 같은 signed 비교를 표현한다. 어떤 방식으로 구현할지는 이미 있는 ALU 신호, 셀 매핑과 경로에 달려 있다. overflow 비트가 다른 연산에도 필요하면 재사용할 수 있고, 부호 분기 방식은 비교에 필요한 조건을 직접 드러낸다.

[Ibex ALU의 비교 경로](https://github.com/lowRISC/ibex/blob/master/rtl/ibex_alu.sv)는 피연산자의 최상위 비트가 같을 때 adder 결과의 부호를 사용하고, 다를 때 signed 여부와 입력 부호로 판정한다. signed와 unsigned의 greater-or-equal 관계를 나누어 설명하는 주석도 있다. 전체 코어의 모든 비교가 이 단순 예제와 같은 폭이라는 뜻은 아니다.

## unsigned 비교와 섞으면 더 조용한 오류가 된다

8비트 11111111은 signed −1이고 unsigned 255다. 00000001과 비교하면 signed에서는 더 작고 unsigned에서는 더 크다. RTL 비교식의 타입 변환이나 한 피연산자의 signed 선언이 예상과 다르면 같은 파형에서 다른 판정이 나올 수 있다.

signed 비교를 뺄셈 carry-out 하나로 대신하는 것도 조건을 확인해야 한다. carry와 borrow는 unsigned 차이의 가중치 관계에 연결되어 있고, signed 순서의 끝점은 부호 표현에 달려 있다. 플래그 이름만 보고 두 의미를 같게 다루면 안 된다.

equal은 별도의 관계다. signed 해석이 달라도 두 비트열이 같다는 조건 자체는 같다. less-than, equal과 greater-or-equal을 결합할 때도 경계값과 같은 값에서 서로 모순되지 않는지 확인할 수 있다.

작은 폭의 모델에서는 모든 입력 쌍을 검사할 수 있다. 8비트라면 256×256=65536쌍이다. 부호가 같은 영역과 다른 영역을 나누어 비교하면 틀린 입력이 어느 조건에 모이는지 이해하기 쉽다. 큰 폭에서도 최소·최대값과 서로 다른 부호의 경계는 직접 넣는 것이 좋다.

signed 비교의 답을 결정하는 것은 감긴 차이의 겉모습만이 아니다. **부호가 다른 입력은 부호로, 같은 입력 부호는 범위 안의 차이로 판단하거나 overflow로 보정해야 한다.** 이 관계를 확인하면 뺄셈 결과는 맞는데 비교가 틀리는 조용한 오류를 잡을 수 있다.

