본문 바로가기
Programming Language

JS33 - 02.원시타입(Primitive Types)

by 양찬우 2021. 5. 31.
728x90

니꼴라스 강의 요약

https://www.youtube.com/watch?v=IMyvCJKZSL8

  • String
  • Numbers: ECMAScript has two built-in numeric types: Number and BigInt 
    • Integers
    • floats
  • Boolean
    • Boolean represents a logical entity and can have two values: true and false. See Boolean and Boolean for more details.
    • True
    • False
  • Null: '존재하지 않음' The Null type has exactly one value: null.
    • 아예 정의되지 않은 변수와는 다르다. Null은 의도적으로 존재하지 않다고 정의된 것.
  • Undefined: 정의되지 않음
    • A variable that has not been assigned a value has the value undefined. See undefined and Undefined for more details.
  • NaN: Not a number
    • 보통 수학 공식 잘 못됐을 때 나옴
  • Symbols (new in ECMAScript 2015)

Javascript에서 숫자가 인코딩 되는 방법

  • JS의 모든 숫자는 부동 소수점이다. (floating point)
> 9007199254740992 + 1 
9007199254740992 //무친 언어..! 
> 9007199254740992 + 2 
9007199254740994

1. Javascript 숫자

  • Javascript는 binary64나 배정밀도(double percision)를 사용한다.
  • float : 단정밀도(single precision) 32비트 double : 배정밀도(double precision) 64비트
  • 숫자는 64비트로 2진법으로 저장된다.
부호(sign)
지수(exponent) 분수(fraction)
(1 bit) (11 bit) (52 bit)
63 62 ~ 52 51 ~ 0

부호(sign) 비트가 0이면 숫자는 양수이고 그렇지 않으면 음수이다. 분수(fraction)에는 숫자의 숫자가 포함되어 있고 지수(exponent)는 점이 어디에 있는지 나타낸다. 주로 이진수를 사용하는 경우가 많고. 부동 소수점은 특이한 케이스이다. 2진수는 접두사로 백분율 부호 (%)를 가진다. 자바스크립트 번호는 2진수로 저장되지만 기본 출력은 10진수로 표현된다. 예제에서는 보통 10진수를 기준으로 설명할 것이다.

2. 분수 (fraction)

유의도는 유의한 자릿수를 나타낸다. 흔히 만티사(Mantissa) 또는 정밀(Precision)라고도 한다. 

과학적 표기법

음수가 아닌 부동 소수점 수를 표현하는 방법:

유효숫자는 자연수를 포함하고 지수는 왼쪽 (음수 지수) 또는 오른쪽 (양수 지수) 점만큼 이동한다.

Javascript 숫자는 유리수를 유효숫자로 사용한다.

f는 52비트 분수이고, 부호를 무시하고 숫자는 유효숫자고 2p를 곱한 것이다.

여기서 p는 지수(나중에 설명할 예정이지만 변환된 지수)이다.

 

f = %101, p = 2
Number: %1.101 × 10^2 = %110.1
f = %101, p = −2 Number: %1.101 × 10^(-2) = %0.01101
f = 0, p = 0 Number: %1.0 × 2 0 = %1

2.2 정수

인코딩된 정수가 몇 비트나 될까?

인코딩이 정수에 대해 몇 비트를 제공하는가? 유효숫자(significand)는 53 자리 숫자르 가지고 있다. 하나는 포인트 앞에, 52는 포인트 뒤에 있다. p = 52를 사용하면 53비트 자연수다. 문제는 가장 높은 비트는 항상 1이라는 점이다. 즉, 우리가 자유롭게 모든 비트를 가질 수 없다. 1은 두 단계로 거쳐 그 제한을 제거한다. 첫째, 가장 높은 비트가 0인 53 비트 번호가 필요한 경우 1이 뒤 따르면 p = 51을 설정한다. 분수의 가장 낮은 비트는 점 다음에 첫 번째 숫자가되고 정수에 대해 0이 된다. 그리고 p = 0과 f = 0이 될 때까지 숫자 1을 인코딩한다.

둘째, 여전히 전체 53비트 인 경우 0을 표현해야한다. 다음 절에서 어떻게 해야 하는지 설명할 예정이다. 부호가 별도로 저장되므로 정수의 크기(절대 값)에 대한 전체 53 비트가 있음을 유의 하도록.

오프셋 바이너리 인코딩에 있는 몇 개의 숫자:

%00000000000 0 → −1023 (lowest number) 
%01111111111 1023 → 0 
%11111111111 2047 → 1024 (highest number) 
%10000000000 1024 → 1 
%01111111110 1022 → −1

3. 지수

지수는 11 비트 길이로, 가장 낮은 값이 0이고 가장 높은 값은 2047 (211 -1)입니다. 음수 지수를 지원하기 위해 이른바 오프셋 바이너리(1023은 0이고 모든 낮은 숫자는 음수이며 높은 숫자는 모두 양수) 인코딩이 사용된다. 이것은 지수에서 1023을 뺀 것을 일반 숫자로 변환한다는 것을 의미한다. 따라서 이전에 사용 된 변수 p 는 e-1023과 같고 유효숫자는 2e-1023을 곱한다.

3.1 특별 지수

가장 낮은 값 (0)과 가장 높은 값 (2047), 두개의 지수 값이 예약되어 있다. 2047의 지수는 무한대 및 NaN (숫자가 아닌) 값에 사용된다. IEEE 754 표준에는 많은 NaN 값이 있지만 Javascript는 모두 이를 하나의 NaN으로 나타낸다. 지수 0는 두 가지 용량에서 사용된다. 먼저 분수(fraction)가 0이면 전체 숫자는 0입니다. 기호가 별도로 저장되므로 우리는 -0과 +0을 모두 가진다.

둘째, 0의 지수는 매우 작은 수 (0에 가깝다)를 나타내는 데에도 사용된다. 그런 다음 분수는 0이 아니어야하며 양수인 경우 숫자는 다음과 같이 계산된다.

%0.f × 2−1022

 

이 표현을 비정규화라고 한다. 앞에서 논의된 표현을 정규화라고 한다.

정규화된 방식으로 나타낼 수 있는 최소 양의(비영점) 수는

%1.0 × 2−1022

 

가장 큰 비정규화 숫자는

%0.1 × 2−1022

 

그렇기에 정규화된 숫자와 비정규화된 숫자 사이를 전환할 때 빈틈이 없다.

 

3.2 요약: 지수

(−1)s × %1.f × 2e−1023 normalized, 0 < e < 2047
(−1)s × %0.f × 2e−1022 denormalized, e = 0, f > 0
(−1))s × 0 e = 0, f = 0
NaN e = 2047, f > 0
(−1)s × ∞ (infinity) e = 2047, f = 0

4. 소수 자릿수

다음 결과에 표시된 것처럼 모든 소수를 JavaScript로 정확하게 표현할 수 있는 것은 아니다.

> 0.1 + 0.2 0.30000000000000004

십진수 소수점 0.1과 0.2도 정확하게 이진 부동 소수점 수로 표현 될 수 없다.

하지만 일반적으로 실제 값과의 편차가 너무 작아 표시 할 수 없다. 추가하면 이러한 편차가 눈에 띄게 된다.

다른 예제:

> 0.1 + 1 - 1 0.10000000000000009

0.1을 나타내는 것은 분수 110(10/100)을 나타내는 도전에 해당한다.

어려운 부분은 분모 10 이다. 이 분모의 인수 분해는 2 x 5입니다.

지수는 정수를 2의 거듭 제곱으로 나눌 수 있지만 5를 얻을 수 있는 방법이 없다.

비교 : 13은 소수 자릿수로 정확하게 표현될 수 없다. 0.333333…로 근사된다.

대조적으로, 소수 분수로 이진 분수를 나타내는 것은 항상 가능하고, 충분한 2를 수집해야한다. (10 개마다 1 개씩 있음).

예제 :

%0.001 = 18 
       = 12 × 2 × 2 
       = 5 × 5 × 5(2×5) × (2×5) × (2×5) 
       = 12510 × 10 × 10 
       = 0.125

4.1 소수 자릿수 비교

따라서 분수 값을 갖는 소수점 입력을 사용하여 작업 할 때 직접 비교하면 안된다. 대신 반올림 오류에 대한 상한선을 고려하라. 이러한 상한을 machine epsilon 이라고 한다. 배정밀도(double precision)에 대한 표준 엡실론 값은 2-53이다.

var epsEqu = function () { // IIFE, keeps EPSILON private 
	var EPSILON = Math.pow(2, -53);
    return function epsEqu(x, y) {
    	return Math.abs(x - y) < EPSILON; 
        }; 
}();

위의 함수는 정상 비교가 부적절한 경우 정확한 결과를 보장한다.

> 0.1 + 0.2 === 0.3 
false 
> epsEqu (0.1 + 0.2, 0.3) 
true 

5. 최대 정수

“x가 최대 정수”라고 하면 어떤 의미일까? 즉, 0 ≤ n ≤ x 범위의 모든 정수 n을 나타낼 수 있고, x보다 큰 정수는 같지 않다. 253번은 그 계산서(?)에 들어맞는다. 이전 모든 숫자는 다음과 같이 나타낼 수 있다.:

> Math.pow(2, 53) 
9007199254740992 
> Math.pow(2, 53) - 1 
9007199254740991 
> Math.pow(2, 53) - 2 
9007199254740990

그러나 다음 정수는 나타낼 수 없다.

> Math.pow(2, 53) + 1 
9007199254740992

상한치인 253의 몇 가지 측면은 놀랍다. 일련의 질문을 통해 그것들을 알아볼 것이다. 한 가지 명심해야할 점은 정수 범위의 하이 엔드에서 한계 자원이 분수라는 것이다. 지수는 여전히 성장할 여지가 있다.

왜 53 비트인가? 크기(부호 제외)에 대해 53비트를 사용할 수 있지만, 분수는 52비트로만 구성된다. 어떻게 그것이 가능할까? 위에서 본 바와 같이, 지수기는 53번째 비트를 제공한다: 이는 분수를 변화시켜 0을 제외한 53개의 비트 수를 모두 나타낼 수 있고 0을 나타내는 특별한 값을 갖는다 (분율 0과 함께).

왜 가장 높은 정수가 253-1이 아닌가? 일반적으로 x 비트는 가장 낮은 숫자가 0이고 가장 높은 숫자는 2x-1임을 의미한다. 예를 들어, 가장 높은 8비트 번호는 255이다. Javascript에서 가장 높은 분율은 실제로 숫자 253-1에 사용되지만, 지수 f = 0, 지수 p = 53(변환 후)의 도움 덕분에 253을 나타낼 수 있다.

%1.f × 2p = %1.0 × 253 = 253

왜 253 보다 높은 숫자를 나타낼 수 있는가?

> Math.pow(2, 53) 
9007199254740992 
> Math.pow(2, 53) + 1  // not OK 
9007199254740992 
> Math.pow(2, 53) + 2 // OK 
9007199254740994 
> Math.pow(2, 53) * 2 // OK 
18014398509481984

253×2는 지수를 사용할 수 있기 때문에 작동한다. 2에 의한 각 곱셈은 단순히 지수 1을 증가시킬 뿐 분율에 영향을 미치지 않는다. 따라서 2의 제곱으로 곱하는 것은 최대 분율에 관한 한 문제가 아니다. 1이 아닌 2를 253에 추가할 수 있는 이유를 확인하려면, 이전 테이블을 추가 비트 53 및 54와 p = 53 및 p = 54의 행으로 확장하면 된다.

행 (p = 53)을 보면 Javascript 숫자가 비트 53을 1로 설정할 수 있음을 분명히 알 수 있다. 그러나 분수 f는 52 비트만 가지므로 비트 0은 0이어야 한다. 따라서 짝수 x만 253 ≤ x < 254 범위로 나타낼 수 있다. 행 (p = 54)에서 그 간격은 254 ≤ x < 255 범위에서 4의 배수로 증가한다.

> Math.pow (2, 54) 
18,014,398,509,481,984 
> Math.pow (2, 54) + 1 
18,014,398,509,481,984 
> Math.pow (2, 54) + 2 
18,014,398,509,481,984 
> Math.pow (2, 54) + (3) 
18,014,398,509,481,988 
> Math.pow (2, 54 ) + 4 
18014398509481988

6. IEEE 754 예외

IEEE 754 에는 은 정확한 값을 계산할 수 없는 다섯 가지 예외(exception)가 존재한다.

  1. Invalid: 잘못된 작업이 수행됨. 예를 들어 음수의 제곱근을 계산하는 경우, NaN을 반환한다.
    > Math.sqrt(-1) 
    NaN​
  2. ** 0으로 나누기**: 음의 무한대나 양의 무한대를 반환한다.
    > 3 / 0 
    Infinity 
    > -5 / 0 
    -Infinity
  3. Overflow: 결과가 너무 커서 표현할 수 없다. 이는 지수가 너무 높다는 것을 의미한다. ( p ≥ 1024). 부호에 따라 양수 및 음수 오버플로가 있고, 플러스 또는 마이너스 무한대를 반환한다.
    > Math.pow(2, 2048) 
    Infinity 
    > -Math.pow(2, 2048) 
    -Infinity​
  4. Underflow: 결과가 너무 작아서 표현할 수 없다. 이는 지수가 너무 낮다는 것을 의미한다.( p ≤ -1023). 비정규화 된 값 또는 0을 반환한다.
    > Math.pow(2, -2048) 
    0​
  5. 부정확함(Inexact): 연산이 부정확한 결과를 생성했다. 분수가 유지 될 유효숫자가 너무 많다. 반올림된 결과를 반환한다.
    > 0.1 + 0.2 
    0.30000000000000004 
    > 9007199254740992 + 1 
    9007199254740992​

3번과 4번은 지수에 관한 것이고, 5번은 분수에 관한 것이다. 3번과 5번의 차이는 매우 미묘하다. 5번에 주어진 두 번째 예제는 분수의 상한선을 초과했다. (정수 계산에서 오버플로우 된다). 그러나 지수의 상한선을 초과하는 것만 IEEE 754에서 오버플로라고 한다.

 

이 포스팅에서 JavaScript가 부동 소수점 숫자를 64 비트로 맞추는 방법을 살펴봤다. 이것은 IEEE 754 표준의 배정밀도에 따라 동작한다. 숫자가 표시되는 방식 때문에 Javascript는 분모의 인수 분해에 2 이외의 숫자가 포함 된 소수 부분을 정확하게 표현할 수 없다는 것을 잊어 버리는 경향이 있다. 예를 들어 0.5 (12)을 표현할 수 있고, 0.6 (35)은 표현 할 수 없습니다. 또한 숫자의 부호, 지수, 분수가 정수를 나타내기 위해 함께 작용한다는 것을 잊어버리는 경향이 있다. 결론적으로 Math.pow (2, 53) + 2 는 표현할 수 있지만 Math.pow (2, 53) + 1 은 표현할 수 없다.

728x90

댓글