Java 심화: 다양한 리터럴 표현식¶
========
문자 리터럴
문자에 대해 코드값을 정의한 표 = Character Set
ASCII(7bit)¶
A -> 100 0001 B -> 100 0010 C -> 100 0011 ... 영어 대소문자 + 특수문자 약 90여개
ISO-8859-1(8bit)¶
ASCII + 서유럽문자 = 200여개 A -> 0100 0001 B -> 0100 0010 C -> 0100 0011 ...
EUC-KR(16bit)¶
한글 2350자 영어 : ISO-8859-1 한글 : 2byte 코드값 가 -> 0xB0A1 각 -> 0xB0A2 똘 -> 0xB6CA 똠 -> 정의되어 있지 않음 똡 -> 정의되어 있지 않음 똣 -> 정의되어 있지 않음 똥 -> 0xB6CB
조합형(16bit)¶
맨왼쪽1비트 + 초성 5비트 + 중성 5비트 + 종성 5비트 = 16비트 약 3만자의 한글을 표현할 수 있으나 국제표준은 아님
MS949(11172자)¶
EUC-KR + 알파
windows95 ~ 지원
똘 -> 0xB6CA
똠 -> 0x8C63
똡 -> 0x8C64
똥 -> 0xB6CB
국제표준이 아님
현재 우리가 사용하는 윈도우 컴퓨터 명령어창에서는 MS949사용
유니코드¶
- 국제 표준 인코딩 규칙이다.
- UCS-2의 경우 영어, 한글 모두 2바이트로 인코딩한다.
- MS949의 모든 한글 문자가 포함되어 있다.
- A(0x0041), B(0x0042), +(0x002b), 가(0xac00), 각(0xac01), ...
- 기존의 EUC-KR과 MS949 코드와 호환되지 않는다.(한글코드 새로 정의)
- JVM은 내부에서 문자열을 다룰 때 UCS-2를 사용한다. 즉 영어, 한글 모두 2바이트 유니코드로 다룬다.
UCS(Universal Coded Character Set; 국제 문자 집합)¶
- 'ISO 10646' 표준을 가리키는 이름이다.
- UCS는 110만개 이상의 문자에 대해 코드 값을 정의하고 있다.
- 그 중에서 첫 65536개(0 ~ 65535 까지의 코드)의 문자 코드가 주로 다.
- 이 범위의 유니코드를 'BMP(Basic Multilingual Plane; 기본 평면)'라 하며 보통 'UCS-2'라 부른다. 즉 2바이트 범위에서 정의한 유니코드라는 의미다.
- 자바가 사용하는 유니코드는 바로 이 'UCS-2'라 불리는 범위의 드이다. 기존 EUC-KR, ISO-8859-1와 호환X
UTF-8¶
- UCS의 코드 값 중에서 00 ~ 7F(127개)까지 ASCII에 해당하는 UCS 그대로 1바이트로 표현한다.(영어를 예전처럼 1byte로) 따라서 ASCII를 기본으로 사용하는 시스템의 경우 UTF-8로 인코딩 이터를 특별한 처리없이 그대로 읽고 쓸 수 있다.
- 그 외의 문자 코드는 규칙에 따라 2바이트 ~ 4바이트까지 변환하여 다.
- UCS의 UTF-8 변환 규칙
000000 ~ 00007F: 0xxxxxxx
000080 ~ 0007FF: 110yyyxx 10xxxxxx
000800 ~ 00FFFF: 1110yyyy 10yyyyxx 10xxxxxx
100000 ~ 10FFFF: 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx - 대부분의 시스템에서 데이터 입출력 할 때 UTF-8을 사용한다.
- 자바에서도 데이터 입출력할 때 주로 UTF-8을 사용하고 있다. 한글은 3byte (유니코드에서는 2byte)
UCS-2 ==> UTF-8¶
한글이 2byte에서 3byte가 됨.
영어는 2byte에서 1byte로 됨.
UCS-2 '가' = 1010 1100 0000 0000 (2byte)
UTF-8 '가' = 1110 1010 1011 0000 1000 0000 (3byte)
JVM 내부에서는 유니코드, 입출력&통신 시에는 UTF-8 사용
| JVM | File/Network | URL Data |
| UCS-2 | UTF-8 | |
| A:0041 | A:41 | A:41 | (16진수로 41 = 10진수 65)
| 가:AC00 | 가:EAB080 | %EA%B0%80 |
-- URL encoding -->
<-- URL decoding --
한글은 변환이 필수
정수 리터럴¶
23
=> 0000 0000 0000 0000 0000 0000 0001 0111
-23
=> 1000 0000 0000 0000 0000 0000 0001 0111 (Sign-magnitude 방식 : 맨 왼쪽 1비트를 부호 비트로 사용한다.)
23 + (-23) = 0 ?
=>
0000 0000 0000 0000 0000 0000 0001 0111
1000 0000 0000 0000 0000 0000 0001 0111
---------------------------------------
1000 0000 0000 0000 0000 0000 0010 1110 = -46
Sign-magnitude 방식은 23 + (-23)의 결과가 옳게 나오지 않는다.
1's complement(1의 보수)
모든 비트를 반전. 0을 1로 1을 0으로
0000 0000 0000 0000 0000 0000 0001 0111 (23)
1111 1111 1111 1111 1111 1111 1110 1000 (-23)
---------------------------------------
1111 1111 1111 1111 1111 1111 1111 1111
+ 1
---------------------------------------
10000 0000 0000 0000 0000 0000 0000 0000 = 0 올림수 1은 버림.
항상 음수를 더할 때 결과에 1을 추가해야 한다.
2's complement(2의 보수)
1의 보수로 저장된 음수 값을 더할 때마다 계산 결과에 1을 추가하는 번거로움을 없애기 위해
미리 음수를 저장할 때 미리 1을 추가해두는 방법
=> 1의 보수 +1 => 2의 보수
1111 1111 1111 1111 1111 1111 1110 1001 (-23)
0000 0000 0000 0000 0000 0000 0001 0111 (23)
---------------------------------------
10000 0000 0000 0000 0000 0000 0000 0000 = 0
그래서 컴퓨터에서 음수를 메모리에 저장할 때는
양수와 음수를 더할 때 정상적인 값이 나오도록
2의 보수 방법으로 음수를 저장한다. (현대 대부분 컴퓨터, C, Java, python 등 대부분 언어에서 2의보수로 음수를 저장)
부동소수점 리터럴¶
고정소수점
System.out.println(3.14159);
부동소수점
System.out.println(0.0314159e2); //0.0314159 * 10^2
System.out.println(31.4159e-1); //0.0314159 * 10^(-1)
System.out.println(314.159e-2); //0.0314159 * 10^(-2)
System.out.println(3141.59e-3); //0.0314159 * 10^(-3)
부동소수점 : 4바이트, 8바이트(기본)
System.out.println(3.141592653589793); //8바이트 부동소수점
System.out.println(3.141592653589793D); //d = double
System.out.println(3.141592653589793d);
4바이트 부동소수점
부동소수점의 경우 큰 값을 작은 메모리에 넣더라도 컴파일 오류가 발생하지 않는다.
값이 잘려서 들어감.
개발자가 잘못된 값을 넣었다는 것을 인지하지 못한다.
System.out.println(3.141592653589793f);
System.out.println(3.141592653589793F);
8바이트 부동소수점 최소/최대값
System.out.println(Double.MAX_VALUE);
System.out.println(Double.MIN_VALUE);
4바이트 부동소수점 최소/최대값
System.out.println(Float.MAX_VALUE);
System.out.println(Float.MIN_VALUE);
System.out.println(3.1415926535f); //값이 구겨진다
System.out.println(3.141592653f); //값이 구겨진다
System.out.println(3.14159265f); //값이 구겨진다
System.out.println(3.1415926f); //값이 구겨진다
System.out.println(3.141592f); //ok
System.out.println(3141.592f); //ok
System.out.println(31415.92f); //ok
System.out.println(314159.2f); //ok
System.out.println(314159.26f); //값이 구겨진다.
개발자가 부동소수점의 정확한 범위를 계산하기가 쉽지 않다.
그래서 2진수 변환 규칙을 참고하여
'유효자릿수'를 통해 값의 범위를 따진다.
4바이트 메모리에 저장할 수 있는 부동소수점은 소수점의 위치에 상관없이 7자리(유효자릿수)
[주의!] 7자리라 하더라도 값이 구겨질 수 있다.
그래서 부동소수점은 메모리에서 꺼낸 값을 내부의 규칙에 따라 JVM이 보정해준다.
100% 정확하게 저장하고 꺼내지 못하는 이유는?
=> 부동소수점을 2진수로 바꿀 때 IEEE-754 규칙에 따라 변경하는데,
이 규칙에서 일부 부동소수점은 2진수로 정확히 변경되지 못하는 문제가 있다.
System.out.println((1.1f + 0.2f) == 1.3f); //false
4바이트 부동소수점 유효자릿수 : 7자리
System.out.println(3.141592f); //ok
System.out.println(3141.592f); //ok
System.out.println(31415.92f); //ok
System.out.println(314159.2f); //ok
System.out.println(314159.26f); //값이 구겨진다.
System.out.println(3.1415926535f); //값이 구겨진다
System.out.println(3.141592653f); //값이 구겨진다
System.out.println(3.14159265f); //값이 구겨진다
System.out.println(3.1415926f); //값이 구겨진다
8바이트 부동소수점 유효 자릿수 : 15자리
System.out.println(3.141592653589793); //ok
System.out.println(31415926.53589793); //ok
System.out.println(314159265358979.3); //ok
16자리인 경우 일부 부동소수점의 값이 제대로 저장되지 않는다.
System.out.println(914159265358979.3); //값이 구겨진다.
System.out.println(91415926.53589793); //ok
System.out.println(9.141592653589793); //ok
15자리인 경우 대부분 부동소수점의 값이 제대로 저장된다.
System.out.println(91415926535897.9); //ok
System.out.println(91415926.5358979); //ok
System.out.println(9.14159265358979); //ok
부동소수점을 저장할 때 완벽하게 저장되지 않는 예
System.out.println(7*0.1);
부동소수점을 2진수로 변환하는 규칙
IEEE 754