본문 바로가기
공부/자바스크립트

[js] ~ tilde와 ~~ double tilde

by 야옹아옹 2023. 1. 8.

프로그래머스 코딩테스트를 풀면서 다른 사람들의 코드를 많이 접하게 됬는 데, 그 중에서 ~~를 사용한 코드를 보고 ~ Bitwise Not (~) 연산자에 대해 정리해보기로 했다.

✨ ~ 연산자

비트를 반전 시킨다. 다른 비트 연산자와 마찬가지로 연산 전에 부호가 있는 32비트 정수로 변경한 후 연산을 진행한다.

자바스크립트는 숫자를 표현할 때, 64 bit 부동소수점 방식을 사용한다.

그러나 모든 비트연산들은 연산 전에 32비트 정수로 변환하고 비트 연산을 진행한다. 연산이 끝난 후, 다시 64비트로 변환한다.

✨ ~를 하나만 쓰는 경우

~N-(N+1)을 반환한다.
const a = 5;     // 0000 0000 0000 0000 0000 0000 0000 0101
const b = -3;    // 1111 1111 1111 1111 1111 1111 1111 1101

console.log(~a); // 1111 1111 1111 1111 1111 1111 1111 1010
// expected output: -6

console.log(~b); // 00000000000000000000000000000010
// expected output: 2

비트를 전부 반전시킨 값을 계산하면 ~N은 -(N+1)이 된다.

✨ ~~를 두 개 쓰는 경우

숫자의 소수점을 없애준다.

위에서 ~ 연산자에대해 설명한 것처럼 비트 연산자를 사용하면 32비트 정수로 변환한 후에 연산을 수행한다.

따라서 ~를 사용하는 순간 이미 소수점이 사라지게된다.

그러나 우리가 원하는 값은 원본값에서 소수점을 없앤 값이기때문에 ~을 두번 사용해 원래 상태로 되돌려주는 것이다.

~~(5.5) // 5
~~(-5.6) // -5 , Math.floor(-5.5)는 참고로 -6 이다.

 

undefinednull을 0으로 변경해준다.
~~(undefined) // 0
~(undefined) // -1

~~(null) // 0
~(null) // -1
undefined와 null에 ~~를 적용하면 0이 되는 이유는 무엇일까?

Bitwise Not ~ 의 자바스크립트 스펙

undefined와 null에 ~~를 적용하면 0이 되는 이유는 자바스크립트 Bitwise NOT의 스펙을 보면 알 수 있다. 

Bitwise Not ~ 스펙

1. 표현식을 평가한다.

2. 입력받은 값을 Numeric으로 변환한다. 이때, undefinedNull은 아래와 같이 변환된다.

ToNumber

3. 변환된 값의 타입을 확인하는 데, NaN0 모두 Number 타입이기 때문에 4번 If문을 타게 된다.

4. Nuber::bitwiseNOT을 실행한다.

Number::bitwiseNOT

5. 비트 연산들은 모두 32비트 정수로 변환 한 후에 비트 연산을 실행한다. 그래서 해당 값에 ToInt32을 해주는 것이다.

6. ToInt32를 보면 NaN, +0인 경우+0을 반환한다.

ToInt32 아래 더 내용이 있지만 2번 문항에서 반환한다.

7. 결론은 ~(undefined)~(null)은 ~0과 값이 같다는 의미다.

~null // -1
~undefined // -1
~0 // -1

8. -1에 다시 ~를 해주면 0이 된다. 따라서 undefined와 null에 ~~를 해주면 0이 된다!

~~(undefined) -> ~~(0) -> ~(-1) -> 0

✨ Math.trunc나 Math.floor 대신 ~~를 써도 되는 걸까?

여러 검색 결과들을 보면 ~~가 Math.trunc보다 속도가 빠르다고 한다. 그러나 그 차이는 브라우저 마다 다르고 매우 큰 편도 아니다. 그리고 나처럼 ~~가 왜 소수점을 제거하는 지 모르는 사람도 많을 거라고 생각한다.

 

보자마자 의미를 파악하기 쉬운 코드가 좋은 코드라고 생각한다. 리액트를 공부하다보니 선언적 프로그래밍 개념이 자주 나온다. 선언적 프로그래밍을 쉽게 이야기하면 어떤 방법으로 돌아가냐를 나타내는 게 아니라 이게 무슨 일을 하는지를 보여주는 프로그래밍 방식이다.

즉, 소수부분을 삭제하기 위해서 ~~를 사용해 어떤 방법을 쓰는 것 보다는 Math.trunc가 소수를 없애는 일을 한다는 코드가 더 좋다고 생각한다.

 

댓글