본문 바로가기
공부/자바

연산자

by xladmt 2024. 5. 24.

연산자 우선순위

후선 부캐 사시 대등 비논 삼대

후치(대입 후 증감) 대입 후 1 추가/감소 a++, a--
선치(대입 전 증감) 1 추가/감소 후 대입 ++a, --a
부정 참이면 거짓, 거짓이면 참 !(논리), ~ (비트)
캐스팅 실수인데 정수로 강제 변환 (int)3.14
사칙연산 곱하기가 더하기보다 우선 *  /  %  >  +  -
시프트 비트 옮기기 ex)001 -> 010 >>  <<  >>>
대소비교 크다 작다 >  <  >=  <=
등호비교 같다 아니다 ==, !=
비트논리 비트에 쓰는 논리 연산자 & > ^ > |
논리 참/거짓에 쓰는 논리 연산자 && > ||
삼항 연산자 if의 축소판 조건 ? 참 : 거짓
대입 모든 대입 연산자 b=1; c+=5;

 

산술 변환

 이항 연산자는 두 피연산자의 타입이 일치해야 연산이 가능하므로, 피연산자의 타입이 서로 다르다면 연산 전에 형변환 연산자로 타입을 일치 시켜야한다. 만약, int타입과 float타입을 덧셈하는 경우, 형변환 연산자를 사용해서 피연산자의 타입을 둘 다 int 또는 float로 일치시켜야 한다.

 대부분 두 피연산자의 타입 중에서 더 큰 타입으로 일치시키는데, 그 이유는 작은 타입으로 형변환하면 원래의 값이 손실될 가능성이 있기 때문이다. 

(* 작은 타입에서 큰 타입으로 형변환하는 경우 자동적으로 형변환이 되므로 연산자를 생략할 수 있다.)

 

  • 산술 변환 규칙
   1. 두 피연산자타입을 같게 일치 시킨다. (보다 큰 타입으로 일치)
       ex) long + int  → long + long → long
             float + int → float + float → float
            double + float → double + double → double

   2. 피연산자의 타입이 int보다 작은 타입이면 int로 변환한다.
       ex) byte + short → int + int → int
            char + short → int + int → int  
  • 1번 규칙은 피연산자의 값손실을 최소화하기 위한 것이고, 2번 규칙은 정수형의 기본 타입인 int가 가장 효율적으로 처리할 수 있는 타입이기 때문이다. 예를 들어, int보다 작은 타입인 char나 short의 표현범위가 좁아서 연산 중에 오버플로우(overflow)가 발생할 가능성이 높기 때문이다.  

 

예제 풀면서 이해해보자!

 

예제 3-7)

byte a = 10;
byte b = 30;
byte c = (byte)(a*b);
System.out.println(c);

=> 실행 결과는? 44

=> 그 이유는? 

byte형(1)에서 int형(4)으로 변환하는 것은 2진수 8자리에서 32자리로 변환하는 것이기 때문에 자료 손실이 일어나지 않는다. 반대로 int형을 byte형으로 변환하는 경우 앞의 24자리를 없애고 하위 8자리(1byte)만을 보존한다. 저장된 값이 10인 경우 값이 작아서 상위 24자리를 잘라내도 원래 값을 유지하는데 지장이 없지만, byte형의 범위인 -128 ~ 127의 범위를 넘는 int형의 값을 byte형으로 변환하면, 원래의 값이 보존되지 않고 byte형의 범위의 값만 가지게 된다. 

 

 

예제 3-8)

int a = 1_000_000;
int b = 2_000_000;
long c = a*b;
System.out.println(c);

=> 실행 결과는? -1454759936

=> 그 이유는?

변수 c의 자료형이 long 타입(8byte)이기 때문에 2X10¹² 을 저장하기에 충분하므로 '2000000000000'이 출력될 것 같지만 아니다. int 타입과 int 타입의 연산결과는 int타입이기 때문이다. a*b의 결과가 이미 int 타입의 값(-1454759936) 이므로 long형으로 자동 형변환되어도 값은 변하지 않는다. 

=> 올바른  결과를 얻으려면?

방법 1) long c = (long)a*b;
방법 2) long c = (long)1000000 * 2000000;
방법 3) long c = 1000000L * 2000000;
방법 4) long c = 1000000L * 2000000L; 

등이 있다.

 

 

예제 3 - 10)

int a = 1000000;

int result1 = a * a / a;
int result2 = a / a * a;

System.out.printf("%d * %d / %d = %d",a,a,a,result1);
System.out.printf("%d / %d * %d = %d",a,a,a,result2);

 

[실행결과]

1000000 * 1000000 / 1000000 = -727         // 오버플로우 발생
1000000 / 1000000 * 1000000 = 1000000

 

= > 그 이유는? 먼저 곱한 후에 나눈 식은 int의 범위를 넘어서기 때문에 예상과 다른 결화가 나온 것이다. 이처럼 같은 의미의 식이라도 연산의 순서에 따라서 다른 결과를 얻을 수 있다는 것에 주의하자!

 

예제 3-22) 비교 연산자

float f = 0.1f;
double d = 0.1;
double d2 = (double)f;

System.out.println(10.0==10.0f);    // true
System.out.println(0.1==0.1f);      // false
System.out.printf("%19.17f%n",f);   // 0.10000000149011612
System.out.printf("%19.17f%n",d);   // 0.10000000000000000
System.out.printf("%19.17f%n",d2);  // 0.10000000149011612
System.out.println(d==f);           // false  
System.out.println(d==d2);          // false
System.out.println(d2==f);          // true
System.out.println((float)d==f);    // true

=> 10.0 == 10.0f는 true인데 0.1==0.1f은 false ?!?!?!

정수형과 달리 실수형은 근사값으로 저장되므로 오차가 발생할 수 있기 때문에 이런 결과를 얻은 것이다. 10.0f는 오차없이 저장할 수 있는 값이라서 double로 형변환해도 그대로 10.0이 되지만, 0.1f는 저장할 때 2진수로 변환하는 과정에서 오차가 발생한다. double타입의 상수인 0.1도 저장되는 과정에서 오차가 발생하지만, float타입의 리터럴인 0.1f보다 적은 오차로 저장된다. (p. 111참고)

 

예제 3-23)

String str1 = "abc";
String str2 = new String("abc");

System.out.println(str2 == "abc");      // false
System.out.println(str2.equals("abc")); // true

=> str2와 "abc"의 내용이 값은데도 '=='fh qlrygkaus, false를 결과로 얻는다. 내용은 같지만 서로 다른 객체라서 그렇다. 그러나 equals() 는 객체가 달라도 내용이 같으면 true를 반환한다. 그래서 문자열을 비교할 때는 항상 equals()를 사용해야 한다는 것을 기억하자. 만일 대소문자를 구별하지 않고 비교하고 싶으면, equalsIgnoreCase()를 사용하면 된다.

 

 

[참고]

 

[Java] 연산자 우선순위가 헷갈려요 ㅠㅠ (Feat. 연산자 간략 설명)

1학기 때 수업으로 배우고 있는 자바 프로그래밍. 연산자에 우선순위가 있다고 하는데요, 사실 괄호가 먼저...

blog.naver.com

'공부 > 자바' 카테고리의 다른 글

배열 선언과 초기화  (0) 2024.05.26
조건문과 반복문  (0) 2024.05.25
상수와 리터럴  (0) 2024.05.22
변수와 자료형  (2) 2024.05.22
Java 란?  (0) 2021.10.14