Java웹 프로그래밍

Java기초2

아리빠 2023. 5. 25. 18:04

● literal: 데이터 표기

- 문자열 → "홍길동" ,  "홍" : 문자열,  " " : 빈 문자열   (문자 개수 + 알파 메모리)

  '홍길동' → 문법 오류 

 

- 문자 → '홍'

 '홍길동'  → 문법오류

 

- 숫자 → 정수  → 4 byte  10          (4 byte 메모리 사용)

                         → 8 byte  10L/10l (8 byte 메모리 사용)

           

          → 부동소수점  → 10f           (4 byte 메모리 사용), 10.2f, 3.14f

                                   → 10.2         (8 byte 메모리 사용), 10.20/10.2d

논리 →  true, false (int 크기, 4bytes 메모리 사용) 단 배열의 경우 1byte 메모리 사용



● 정수 리터럴


● 문자 리터럴



1 비트: 2**1
  - 음수가 없는 경우: 0, 1
2 비트: 2**2
  - 음수가 없는 경우: 00(0), 01(1), 10(2), 11(3) (0 ~ 3)
  - 음수가 있는 경우: 00(0), 01(1), 10(-2), 11(-1) (-2 ~ 1)
4 비트: 2**4
  - 음수가 없는 경우: 0 ~ 2**4 - 1 = 0 ~ 15
  - 음수가 있는 경우: -2**3 ~ 2**3 - 1 = -8 ~ 7
8 비트: 2**8
  - 음수가 없는 경우: 2**8 = 0 ~ 2**8-1 = 0 ~ 255
  - 음수가 있는 경우: -2**7 ~ 2**7-1 = -128 ~ 127
16비트: 2**16
  - 음수가 없는 경우: 0 ~ 2**16-1 = 0 ~ 65535
  - 음수가 있는 경우: -2**15 ~ 2**15-1 = -32768 ~ 32767
32비티: 2^32
  - 음수가 없는 경우: 0 ~ 2**32 - 1 = 4,294,967,296
  - 음수가 있는 경우: -2**31 ~2**31 - 1 = -2,147,483,648 ~ 2,147,483,647
64비트: 2^64 =
  - 음수가 있는 경우: 약 -900경 ~ 900경

## 정리
- 4바이트 정수 리터럴
  예) 100

- 8바이트 정수 리터럴
  정수 뒤에 접미사 L 또는 l을 붙인다.
  예) 100L (주로 사용)
  예) 100l


 

package com.eomcs.lang.ex03;

//# 정수 리터럴 - 메모리에 저장하는 방법
//- 컴퓨터에서는 값을 저장할 때 전기 신호(예: RAM) 또는 자기 신호(예: HDD)로 저장한다.
//- 값은 비트로 표현되고, 각 비트는 전기가 있거나 없는 두 상태로 표현한다.
//- 비트의 두 가지 상태를 표현할 때 2진수의 1과 0을 사용한다.
//- 따라서 값을 메모리에 저장하려면 2진수로 표현할 수 있어야 한다.
//
//* 참고!
//- 그림이나 사진(BITMAP), 음성(WAV), 색상(RGB)을 저장할 때도 2진수로 표현한다.
// 

public class Exam0240 {
  public static void main(String[] args) {
    System.out.println(5 - 3);
    System.out.println(5 + (-3));
  }
}

//## 정수를 2진수로 표현하는 방법
//1) 부호-크기/절대값(Sign-Magnitude)
//- 부동 소수점에서 가수부(significand or mantissa)를 저장할 때 사용한다.
//- 맨 왼쪽 1비트를 부호 비트로 사용한다. 양수는 0, 음수는 1 이다. 
//- 나머지 비트는 절대값(magnitude)으로 저장한다.
//    8비트 = 1비트(부호) + 7비트(절대값)
//  예) +24 => |+24| = 24 ---> 0001 1000
//  예) -24 => |-24| = 24 ---> 1001 1000
//- 수의 범위(8비트 기준): -127 ~ + 127
//   0111 1111 (127)
//   0111 1110 (126)
//   0111 1101 (125)
//      ...
//   0000 0001 (1)
//   0000 0000 (+0)
//   1000 0000 (-0)
//   1000 0001 (-1)
//      ...
//   1111 1101 (-125)
//   1111 1110 (-126)
//   1111 1111 (-127)
//- 단점
//  - 두 개의 0(+0, -0)이 존재한다.
//  - 양수와 음수를 더했을 때 옳지 않은 값이 나온다.
//     예) 4비트일 경우, 1 + -1 = ?
//         0001(+1) + 1001(-1) = 1010 (-2) <-- 계산 결과가 옳지 않다.
//  - 빼기를 처리하는 컴퓨팅 회로를 별도로 설계해야 하므로 하드웨어가 복잡해진다.
//- 장점
//  - 이해하기 쉽다!
//
//2) 1의 보수(Ones' Complement)
//- 모든 비트를 반대 값으로 바꾼다.
//  예) +24 => 0001 1000
//  예) -24 => 1110 0111
//- 수의 범위(8비트 기준): -127 ~ +127
//   0111 1111 (127)
//   0111 1110 (126)
//   0111 1101 (125)
//      ...
//   0000 0001 (1)
//   0000 0000 (+0)
//   1111 1111 (-0)
//   1111 1110 (-1)
//      ...
//   1000 0010 (-125)
//   1000 0001 (-126)
//   1000 0000 (-127)
//- 단점
//  - 두 개의 0(+0, -0)이 존재한다.
//  - 두 수를 더한 후 비트 크기를 초과한 1 값을 다시 맨 뒤에 더해야만 옳은 값이 된다.
//    예) 4비트일 경우,
//       0001(+1)
//       1110(-1)
//       --------
//       1111(-0) <--- 음수 0과 양수 0을 다뤄야 하는 것이 번거롭다.
//
//       0101(+5)
//       1100(-3)
//       --------
//     1 0001(1) <--- 옳지 않은 값.
//     +    1    <--- 4비트를 초과하는 값을 다시 맨 뒤에 더함.
//     ---------
//       0010(2) <--- 옳은 값!
//
//3) 2의 보수(Two's complement)
//- 자바에서 음수를 저장하는 방법이다.
//- 1의 보수의 문제점을 해결하기 위해 등장한 방법.
//- 음수 0을 없앰으로써 -128까지 표현할 수 있음.
//- 2의 보수를 만드는 방법 1:
//  - 모든 비트를 반대 값으로 만든 다음 1을 더한다.
//  예) 0010 1001(+41)
//      1101 0110(1의 보수)
//              1
//      ---------
//      1101 0111(-41)
//- 2의 보수를 만드는 방법 2:
//  - 오른쪽에서부터 1을 찾는다.
//  - 찾은 1의 왼쪽편에 있는 모든 비트를 반대 값으로 바꾼다.
//    예) 0010 1001(41) => 1101 0111(-41)
//                ^                ^
//    예) 0010 1100(44) => 1101 0100(-44)
//              ^                ^
//- 2의 보수를 만드는 방법 3:
//  - 2^n(8비트일 경우 2^8 = 256)에서 음수 값 만큼 뺀다.
//    예) -41 => 256 - 41 = 215 = 1101 0111
//    예) -44 => 256 - 44 = 212 = 1101 0100
//- 수의 범위(8비트 기준): -128 ~ +127
//   0111 1111 (127)
//   0111 1110 (126)
//   0111 1101 (125)
//      ...
//   0000 0010 (2)
//   0000 0001 (1)
//   0000 0000 (+0)
//   1111 1111 (-1)
//   1111 1110 (-2)
//      ...
//   1000 0011 (-125)
//   1000 0010 (-126)
//   1000 0001 (-127)
//   1000 0000 (-128)
//- 장점
//  - 양수와 음수의 덧셈이 가능하다.
//  - 음수 0이 없다. 0에 대한 표현이 한 가지이다.
//
//4) K-초과(Excess-K )
//- 부동 소수점의 지수부(exponent)를 저장할 때 사용한다.
//- 오프셋 바이너리(offset binary) 또는 바이어스된 표기법(biased representation) 이라고도 한다.
//- K를 바이어스 값이라 부르며, 표현하려는 값에 더할 때 사용한다.
//    표현하려는 값 + 초과 값(K) = 결과
//- 바이어스 값(K)을 구하는 공식:
//    K = 2^(비트수 - 1)
//  예) 8비트일 경우 ---> K = 2^(8 - 1) = 2^7 = 128, 결과 = 128 + 값
//   1111 1111 = 128 + 127
//   1111 1110 = 128 + 126
//   1111 1101 = 128 + 125
//      ...
//   1000 0001 = 128 + 1
//   1000 0000 = 128 + 0
//   0111 1111 = 128 + (-1)
//      ...
//   0000 0010 = 128 + (-126)
//   0000 0001 = 128 + (-127)
//   0000 0000 = 128 + (-128)
//
//- IEEE 부동소수점 표준에서는 다음의 공식을 사용한다.
//    K = 2^(비트수 - 1) - 1
//  예) 8비트일 경우 ---> K = 2^7 - 1 = 127, 결과 = 127 + 값
//   1111 1111 = 127 + 128
//   1111 1110 = 127 + 127
//   1111 1101 = 127 + 126
//   1111 1100 = 127 + 125
//      ...
//   1000 0000 = 127 + 1
//   0111 1111 = 127 + 0
//   0111 1110 = 127 + (-1)
//      ...
//   0000 0010 = 127 + (-125)
//   0000 0001 = 127 + (-126)
//   0000 0000 = 127 + (-127)
//
//- 바이어스 방식으로 데이터를 저장할 때의 이점?
//  - 모든 비트가 0일 때 최소 값을 가지고, 모든 비트가 1일 때 최대 값을 갖는다.
//  - 이런 이유로 작은 값에서 큰 값으로 정렬되는 결과를 낳는다.
//  - 모든 비트가 정렬된 상태이기 때문에 부동소수점이든 정수이든 상관없이 값을 비교할 때 속도가 빠르다.
//  - '부호-크기', '1의 보수', '2의 보수'와 같은 방법으로 값을 표현할 경우, 비트들이 순차적으로 정렬되지 않는다.
//
//## 2의 보수를 사용하여 8비트 정수를 저장하기
//  예) 10(10진수)  => 0000 1010(2진수)
//  예) -10(10진수) => 1111 0110(2진수)
//
//## -10을 2진수로 표현하기
//1) 10을 2진수로 바꾼다.
//   +10 => 0000 1010
//2) 1의 보수로 바꾼다. 즉 0은 1로 1은 0으로 바꾼다.
//   0000 1010 => 1111 0101
//3) 2의 보수로 바꾼다. 1의 보수에 1을 더한다.
//     1111 0101
//   + 0000 0001
//     ---------
//     1111 0110
//
//## 음수가 2의 보수로 저장되었을 때 이점
//- 덧셈으로 빼기를 수행할 수 있기 때문이다.
//  예) 10 - 7 = 10 + (-7) = 3
//      10      => 0000 1010
//      7       => 0000 0111
//      -7      => 1111 1000 + 1 = 1111 1001
//
//      0000 1010 (10)
//    + 1111 1001 (-7)
//    -----------------
//    1 0000 0011 (3)  => 8비트를 넘어가는 값은 버린다.
//

 
    // 1)Sign-magnitude : 맨 앞비트로 음수 표시
    // => 1000 0000 0000 0000 0000 0000 0001 0111 (-23)
    // => 23 + (-23) = 0
    // => 정말?
    //    0000 0000 0000 0000 0000 0000 0001 0111 (23)
    //    1000 0000 0000 0000 0000 0000 0001 0111 (-23)
    //    ---------------------------------------
    //    1000 0000 0000 0000 0000 0000 0010 1110 = -46 
    //    이 방식은 23 + (-23) 의 결과가 옳게 나오지 않는다.
    // 

    // 2) 1's complement(1의 보수)
    // => 모든 비트를 1이 되는 수로 바꾼다. 즉 0을 1로, 1을 0으로 바꾼다.
    // => 1111 1111 1111 1111 1111 1111 1110 1000 (-23)
    // => 23 + (-23)
    //    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 = 0이 아니다.
    //   +                                      1 <= 1을 추가로 더한다. 
    //    --------------------------------------- 
    //   10000 0000 0000 0000 0000 0000 0000 0000 = 0
    //    음수를 더할 때는 항상 결과에 1을 추가해야 한다.
    // 
    // 3) 2's complement(2의 보수)
    // => 1의 보수로 저장된 음수 값을 더할 때 마다 
    //    계산 결과에 1을 추가하는 번거로움을 없애기 위해
    ///   음수를 저장할 때 미리 1을 추가해 두는 방법
    // => 1의 보수 + 1 => 2의 보수
    // => 1111 1111 1111 1111 1111 1111 1110 1001 (-23)
    // => 23 + (-23)
    //    0000 0000 0000 0000 0000 0000 0001 0111 (23)
    //    1111 1111 1111 1111 1111 1111 1110 1001 (-23)
    //    ---------------------------------------
    //   10000 0000 0000 0000 0000 0000 0000 0000 = 0
    //   그래서 컴퓨터에서 음수를 메모리에 저장할 때는
    //   양수와 음수를 더할 때 정상적인 값이 나오도록 
    //   2의 보수 방법으로 음수를 저장한다.

	//## Exponential 기호 사용하기
    //- e 기호를 사용하면 소수점의 위치를 조정할 수 있다.
    //- 소수점의 위치를 움직인다고 해서 "부동소수점(floating point)"이라 부른다. 
    //- 표기법
    //    숫자e지수 또는 숫자E지수 
    System.out.println(0.0314e2); // 0.0314 * 10의2승 = 3.14
    System.out.println(0.314e1); // 0.314 * 10의1승 = 3.14
    System.out.println(31.4e-1); // 31.4 * 10의-1승 = 3.14
    System.out.println(314e-2); // 314 * 10의-2승 = 3.14

    //## 8바이트 크기의 부동소수점
    //- 숫자 맨 뒤에 d 또는 D를 붙인다.
    //- 생략해도 된다.
    System.out.println(3.14);
    System.out.println(3.14d);
    System.out.println(3.14D);

    //## 4바이트 크기의 부동소수점
    //- 숫자 맨 뒤에 f 또는 F를 붙인다.
    System.out.println(3.14f); // 주로 소문자 f를 많이 붙인다.
    System.out.println(3.14F);
  }
}

//## 정리
//4바이트 부동소수점
//  3.14F
//  3.14f (주로 사용)
//  31.4e-1f
//  31.4e-1F
//  
//8바이트 부동소수점
//  3.14
//  31.4e-1

    //## 4byte(float) 부동소수점의 유효자릿수
    //소수점을 뺀 후 7자리 숫자까지는 거의 정상적으로 저장된다.
    System.out.println(999.9999f);
    System.out.println(999999.9f);
    System.out.println(9.999999f);
    System.out.println("----------------------------");

    //유효자릿수가 7자리를 넘어가는 경우 값이 잘려서 저장될 수 있다.
    System.out.println(987654321.1234567f);
    System.out.println(9.876543211234567f);
    System.out.println(987654321123456.7f);
    System.out.println("----------------------------");

    //## 8byte(double) 부동소수점의 유효자릿수
    //소수점을 뺀 후 16자리 숫자까지는 거의 정상적으로 저장된다.
    System.out.println(987654321.1234567);
    System.out.println(9.876543211234567);
    System.out.println(987654321123456.7);
    System.out.println("----------------------------");

    //유효자릿수가 16자리를 넘어가는 경우 값이 잘려서 저장될 수 있다.
    System.out.println(987654321.12345678);
    System.out.println(9.8765432112345678);
    System.out.println(987654321123456.78);
    System.out.println("----------------------------");

    //## 부동소수점을 저장할 때 정확하게 저장되지 않는 예
    System.out.println(7 * 0.1); //결과: 0.7000000000000001
    //- 이유
    //  - IEEE-754 규격에 따라 부동소수점을 2진수로 바꾸다보면
    //    정확하게 2진수로 딱 떨어지지 않는 경우가 있다.
    //    CPU, OS, 컴파일러, JVM의 문제가 아니다.
    //  - IEEE-754의 방법에 내재되어 있는 문제다.
    //- 해결책
    //  - 시스템에서 필요한 만큼 소수점 이하 자리수를 적절히 짤라 사용하라!
  }
}

//## 결론!
//- 32비트(float) 메모리에 부동소수점을 저장할 때는 유효자릿수 7자리까지는 거의 가능하다. 
//- 64비트(double) 메모리에 부동소수점을 저장할 때는 유효자릿수 15자리까지는 거의 가능하다.
//- 그래서 32비트 float을 단정도(single-precision)라 부르고,
//  64비트는 두 배 정밀하게 값을 저장한다고 해서 배정도(double-precision)이라 부른다.
//
//## 메모리 비트와 2진수
//- 메모리의 각 비트는 전기의 on/off 상태를 나타낸다.
//- 비트의 on 상태를 1, off 상태를 0으로 표현한다.
//- 그래서 메모리의 비트 상태를 표현할 때는 2진수로 나타낸다.
//
//## 메모리에 값을 저장하기
//- 메모리는 비트로 되어 있기 때문에 어떤 유형의 값이든 2진수로 변환할 수 있다면 메모리에 저장할 수 있다.
//- 예) 빛을 저장하기
//  - 카메라 렌즈로 들어온 빛을 메모리에 저장하려면 2진수로 변환해야 한다.
//  - 이런 역할을 하는 것이 이미지 센서이다.
//- 예) 소리를 저장하기
//  - 소리도 마찬가지이다. 메모리에 저장하려면 2진수로 변환해야 한다.
//  - 마이크로 들어온 음파를 ADC를 통해 디지털 신호, 즉 2진수로 변환한다.
//
//## 부동소수점이 메모리에 저장되는 원리
//- 부동소수점도 메모리에 저장하려면 2진수로 표현할 수 있어야 한다.
//- 부동소수점을 2진수로 변환하는 것을 "정규화(nomalized)"라 부른다.
//- 이런 정규화 규칙을 정의한 문서가 "IEEE-754"이다.
//- 부동소수점을 2진수로 바꿀 때, 가수부와 지수부로 분리하여 변환한다. 
//- 가수부는 'Sign-Magnitude 방식'으로 변환하고, 지수부는 'Excess-K 방식'으로 변환한다. 
//
// ### IEEE-754
// - 부동소수점을 32비트와 64비트로 표현하는 방법에 대해 정의하고 있다.
// - 32비트로 표현하는 방법을 "single-precision(정밀도 1; 단정도)"이라 한다.
// - 64비트로 표현하는 방법은 32비트에 대비해 두 배 가량 더 정밀하게 표현할 수 있다고 해서 "double-precision(정밀도가 두 배; 배정도)"이라 한다.
// 
// ### 부동소수점을 2진수로 표현하는 방법
// - 자바에서 부동소수점의 정규화는 "IEEE 754-1985" 명세에 따른다.
// - 메모리 크기와 비트의 구성
//   => 32비트 float 타입(32-bit single-precision; 단정도)
//     [부호비트(1)][지수부(8)][가수부(23)]
//   => 64비트 double 타입(64-bit double-precision; 배정도)
//     [부호비트(1)][지수부(11)][가수부(52)]
// - 부호비트(sign bit)
//   음수는 1, 양수는 0.
// - 지수(exponent)
//   127 bias를 사용한다. 즉 2의 지수 값에 127을 더한 결과 값을 사용한다.
// - 가수(fraction/mantissa)
//   sign-magnitude 방식으로 저장한다.
//   1.xxxx 값에서 소수점 왼쪽에 있는 1을 제외한 나머지 수를 사용한다.
//   가수부에 남는 비트가 있다면 0으로 채운다.

// ### 실수 값을 정규화하는 방법 = 실수 값을 32비트 2진수로 만드는 방법
// - 예: 12.375(10진수)
//
// 1) 소수점 앞의 정수 값을 2진수로 변환한다.
//   12(10진수)
//   = 1100(2진수)  
// 
// 2) 소수점 뒤의 값을 2진수로 변환한다.
// - 변환 규칙
// - 소수점을 2로 곱하여 나온 결과에서 정수 부분만을 차례대로 표기한다.
// - 소수 부분이 0이거나 반복되면 계산을 멈춘다.
// - 예: 0.375(10진수)
//   0.375 * 2 = 0.75  --> 0
//   0.75 * 2  = 1.5   --> 1
//   0.5 * 2   = 1.0   --> 1
//   => 0.011(2진수)
//
// 3) 2진수 바꾼 최종 결과
//   12.375(10진수)
//   = 12(10진수) + 0.375(10진수)
//   = 1100(2진수) + 0.011(2진수)
//   = 1100.011(2진수)
//   = 1*2^3 + 1*2^2 + 0*2^1 + 0*2^0 + 0*2^-1 + 1*2^-2 + 1*2^-3
//   = 1*8 + 1*4 + 0*2 + 0*1 + 0*0.5 + 1*0.25 + 1*0.125
// 
// 4) 정규화 
// - 소수점의 위치를 조정하여 가수부와 지수부를 분리한다.
// - IEEE 754 명세는 다음과 같은 형식으로 가수부와 지수부를 나눈다.
//   1.x1x2x3x4...x23(2진수) * 2^e
//   => 소수점 왼쪽에 1만 남도록 소수점을 이동한다.
//   => 소수점 왼쪽은 무조건 1이기 때문에 저장하지 않고 버린다.
//   => 따라서 소수점 오른쪽 수만 가수부에 놓는다.
//      즉 x1, x2 등은 가수부 1번 비트부터 23번 비트까지를 의미한다.
//   => 23번 비트까지 채우지 못하면 나머지 비트는 0으로 채운다.
// - 예)
//   1100.011(2진수)
//   = 1.100011(2진수) * 2^3
//   가수부 => 100011(2진수)
//   지수부 => 3 + 127(bias) = 130(10진수) = 10000010(2진수)
//
// 5) 32비트로 표현하기
//   [0][10000010][10001100000000000000000]
//   => 0100_0001_0100_0110_0000_0000_0000_0000
//   => 0x41460000
//
//주의!
//- 유효 자릿수의 부동소수점이라도 정규화할 때 2진수로 딱 떨어지지 않은 경우가 있다.
//- 예) 2.127
//2 => 0010
//0.127 => 
//0.127 * 2 = 0.254 --> 0
//0.254 * 2 = 0.508 --> 0
//0.508 * 2 = 1.016 --> 1
//0.016 * 2 = 0.032 --> 0
//0.032 * 2 = 0.064 --> 0
//0.064 * 2 = 0.128 --> 0
//0.128 * 2 = 0.256 --> 0
//0.256 * 2 = 0.512 --> 0
//0.512 * 2 = 1.024 --> 1
//0.024 * 2 = 0.048 --> 0
//....
//이처럼 2진수로 완벽히 표현할 수 없는 수가 있다.
//0.00000....1 의 오차가 있다.
//그래서 부동소수점은 정수와 다르게 정확하게 2진수로 변환할 수 없는 경우가 있다.
//즉 메모리에 정확하게 저장되지 않을 수 있다.

'Java웹 프로그래밍' 카테고리의 다른 글

Java기초 4  (0) 2023.05.30
Java기초 3  (0) 2023.05.26
Java 기초  (0) 2023.05.24
프로그래밍 개론 2  (0) 2023.05.23
프로그래밍 개론 1  (0) 2023.05.22