home > bootcamp > lg-eureka > [lg-eureka] 부트 캠프 2일차

[lg-eureka] 부트 캠프 2일차
bootcamp lg-eureka 2일차

intro : 부트캠프 2일차 내용 정리 및 기록.

수업 내용 기록

오버 플로우, 언더플로우

// 정수
public class Test {
    public static void main(String[] args) {
        int a = 128;
        byte b = (byte) a;
        System.out.println("b = " + b); // -128 (오버 플로우)

        a = -129;
        b = (byte) a;
        System.out.println("b = " + b); // 127 (언더 플로우)
        
    }
}

// 실수
public class Test {
    public static void main(String[] args) {
        // 오버플로우 예제
        float floatMax = Float.MAX_VALUE; // 3.4028235E38
        System.out.println("Float MAX: " + floatMax);

        float floatOverflow = floatMax * 2; // 오버플로우
        System.out.println("Float Overflow: " + floatOverflow); // Infinity

        double doubleMax = Double.MAX_VALUE; // 1.7976931348623157E308
        System.out.println("Double MAX: " + doubleMax);

        double doubleOverflow = doubleMax * 2; // 오버플로우
        System.out.println("Double Overflow: " + doubleOverflow); // Infinity

        // 언더플로우 예제
        float floatMin = Float.MIN_VALUE; // 1.4E-45 (가장 작은 양수)
        System.out.println("Float MIN (Positive): " + floatMin);

        float floatUnderflow = floatMin / 2; // 언더플로우
        System.out.println("Float Underflow: " + floatUnderflow); // 0.0

        double doubleMin = Double.MIN_VALUE; // 4.9E-324 (가장 작은 양수)
        System.out.println("Double MIN (Positive): " + doubleMin);

        double doubleUnderflow = doubleMin / 2; // 언더플로우
        System.out.println("Double Underflow: " + doubleUnderflow); // 0.0
    }
}

Float MAX: 3.4028235E38
Float Overflow: Infinity
Double MAX: 1.7976931348623157E308
Double Overflow: Infinity
Float MIN (Positive): 1.4E-45
Float Underflow: 0.0
Double MIN (Positive): 4.9E-324
Double Underflow: 0.0

정수 연산

산술 연산을 정확하게 계산 하려면 실수 타입을 사용하지 않는 것이 좋음. 정확한 계산이 필요하면 정수 연산으로 변경 (double, float으로 정확한 실수 연산을 하는건 비추천, Math Class의 BigDecimal을 이용하는게 좋음)

나눗셈 연산에서 예외 방지하기

나눗셈 또는 나머지 연산에서 좌측 피연산자가 정수이고 우측 피연산자가 0일 경우 ArithmeticException 발생, 좌측 피연산자가 실수이거나 우측 피연산자가 0.0 또는 0.0f이면 예외가 발생하지 않고 연산의 결과는 무한대 또는 NaN이 됨

비트 이동 연산자

  1. << (왼쪽 시프트)
    • 모든 비트(부호 비트 포함)를 왼쪽으로 이동.
    • 오른쪽 빈자리는 항상 0으로 채움.
  2. >> (부호 있는 오른쪽 시프트)
    • 모든 비트를 오른쪽으로 이동, 부호 비트는 이동하지 않음.
    • 왼쪽 빈자리는 부호 비트 값으로 채움 (1 또는 0).
  3. >>> (부호 없는 오른쪽 시프트)
    • 모든 비트를 오른쪽으로 이동, 부호 비트 포함.
    • 왼쪽 빈자리는 항상 0으로 채움.

4조 workshop 토의 내용 기록

논의 1. 쇼트서킷이 뭘까? (feat. && || & |)

쇼트서킷이란? 논리연산자에서 좌측 피연산자 만으로도 결과가 확정된 경우, 굳이 우측 피 연산자의 계산 과정을 진행하지 않는 기능이다. 쇼트서킷의 연산 과정을 통해 불필요한 연산을 생략함으로써 성능적으로 이점을 볼 수 있다. 자바에서 || 이 대표적으로 쇼트서킷을 활용할수 있는 논리 연산자로 볼 수 있는데, 다음과 같은 예시에서 쇼트 서킷이 뭔지 알 수 있다.

public class Test {
    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        
        x++; // x = 11 
        y--; // y = 19
		
        if (x == 11 || y == 20) {
            System.out.println("쇼트서킷을 통해 y값이 19이지만, 조건식의 연산이 최종적으로 true로 연산되어 출력문이 출력된다.");
        } else {
            System.out.println("쇼트서킷 실패");
        }
    }
}

그렇다면 비트 연산자인 &| 는 왜 쇼트서킷이 적용이 안될까? 그건 바로 비트 연산자는 좌항과 우항의 자릿수의 비트에 대한 비트연산을 적용하는 방식으로 동작하기 떄문이다.

  0101 (a)
& 0011 (b)
------
0001 (결과: 1)
===================
  0101 (a)
| 0011 (b)
------
0111 (결과: 7)

논의 2. yeild는 어디에 사용하는 키워드일까?

확인문제 2번의 정답 중 yield(반환하다, 돌려주다) 라는 키워드에 대한 논의를 진행하였다. 다음과 같은 예시에서 yield의 사용법을 엿볼수 있었는데, 간단하게 설명해보자면 switch case문에서 결과값을 반환할 때 사용하는 키워드 값이라고 볼 수 있다.

함수에 값을 돌려준다는 개념으로 접근해야하는데, return은 메서드를 종료하며 값을 반환하는 것이지만, yield는 switch case 문의 값을 시작하는 지점으로 값을 돌려준다.

public class Test {
    public static void main(String[] args) {
        String grade = "B";
        int score = switch (grade) {
            case "A" -> 100; // 단순 리터럴 반환
            case "B" -> {
                int deduction = 20;  // 블록 내에서 연산 수행
                int result = 100 - deduction;
                yield result;        // 연산 결과 반환
            }
            default -> 60;           // 기본값 반환, yeild 사용시에 defult 필수
        };
        System.out.println(score); // 출력: 80
    }
}

화살표(->)를 사용하여 단순 값을 반환할 때는 yield 없이 바로 값을 지정할 수 있다.

논의3. printf는 뭘까?

정수를 표현하여 출력할떄는 %d, 실수를 표현하여 출력할때는 %f, 문자를 표현하여 출력할때는 %s, 시간을 표현하여 출력하는 %t, 결국 printf의 사용은 지시자를 통해 변수의 값을 표현하는 개념이 적용된다.

public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 6; j++) {
                // 값 출력시에, i,j의 값을 정수값으로 표현해주는 서식
                // %d: 정수, %s: 문자열 %t : 시간 등등
                System.out.printf("%d %d ", i, j);
            }
            System.out.println();
        }
    }
}

논의4. 소수점 표현에 대한 이야기

소수를 표현하는 타입에는 float 과 double 이 있지만 정밀도의 문제로 float은 7자리까지, double은 15자리까지 표현된다(유효숫자). 하지만 정확한 소수관련된 데이터를 다루기 위해서는 Bigdecimal 클래스를 사용한 로직을 구성해야 정확한 계산을 진행할 수 있다. 왜냐하면 float과 double은 소수를 이진수로 표현하는 과정에서 대부분 무한 반복 소수가 되어 근사치 값으로 저장되기 때문이다.

예를들어 0.1을 2진수로 표현하려면 어떻게 표현되는지 아래 예시를 통해 알아보자.

0.1 * 2 = 0.2 (정수 부분은 2진수로 기록, 소수 부분은 다음 계산에 사용)

 과정을 반복.... 아래와 같은 연산이 반복됨
 과정의 반복이 종료되려면, 결과값이 0 되어야 종료.

0.1 * 2 = 0.2  정수 부분 : 0
0.2 * 2 = 0.4  정수 부분 : 0
0.4 * 2 = 0.8  정수 부분 : 0
0.8 * 2 = 1.6  정수 부분 : 1
0.6 * 2 = 1.2  정수 부분 : 1

결론적으로 0.1 2진수로 표현하려면 0.0001100...(무한반복)
0.1 2진수로 정확히 표현되지 않고, 무한 반복 소수가 된다.
결론적으로 컴퓨터는 위의 무한반복되는 값을 근사값으로 저장한다.

Bigdecimal에 대한 사용을 진행하면 내부적으로 Integer 끼리의 연산을 진행하고 차후 마지막 결과값을 반환시에 어느 부분에서 소수점을 찍어야 하는지 처리되기때문에 정확한 연산이 가능하게 된다.

위와 같은 장점이 존재하지만, 단점으로는 많은 메모리와 오버헤드를 유발하기에 대량의 숫자 연산을 처리할 때 문제가 될 수 있다.