2014년 9월 4일 목요일

Java 연산자(그 밖의 연산자)

6. 그 외의 연산자

6.1 삼항 연산자 ? :


삼항 연산자는 세 개의 피연산자를 필요로 하기 때문에 삼항 연산자입니다. 조건식과 조건식이 참일 때와 거짓 일 때 반환되는 값 이 세가지가 삼항연사자의 피연사자입니다. 삼항 연산자의 조건식에는 연산결과가 ture 또는 false인 식이 사용되어야 합니다.
 조건식의 연산결과가 true이면 식 1을 결과로 얻고 false이면 식2를 결과로 얻습니다.

(조건식) ? 식1 : 식2
result = (x>0)? x : -x;

삼항 연산자는 if문으로 바꿔 쓸 수 있으며, 간단한 if문 대신 삼항 연산자를 사용하면 코드를 간단히 할 수 있습니다.
 위의 삼항연산자를 if문으로 바꾸면 다음과 같이 됩니다

if(x>0){
result = x;
}else{
result=-x;
}

삼항연산자

public class Operator27 {

public static void main(String[] args) {

int x = 10;
int y = -10;

int X = ( x>=0 ? x : -x);
int Y = ( y>=0 ? y : -y);

System.out.println("x=10일 때. x의 절대값은 "+X);
System.out.println("y=10일 때, y의 절대값은 "+Y);

}
}

실행결과

x=10일 때. x의 절대값은 10
y=10일 때, y의 절대값은 10

삼항 연산자를 이용해서 변수의 절대값을 구하는 예제입니다.
삼항 연산자를 대신 조건문을 사용하면
int X=0;
if(x>=0){
X=x}else{
X=-x;
}

6.2 대입 연산자 =,op=


대입 연산자는 변수에 값 또는 수식의 연산결과를 저장하는데 사용됩니다. 대입 연산자의 왼쪽에는 반드시 변수가 위치하고 오른쪽에는 리터럴이나 변수 또는 수식이 옵니다.

int x = 0;
x = 3
x = x+3;
7 = x+4;  // 대입 연산자의 왼쪽 피연산자가 변수가 아니기 때문에 에러가 난다.
final X = 3; //final을 붙이면 상수가 됩니다.
X = 10  // 상수의 값은 바꿀 수 없습니다.

변수 앞에 키워드 'final'을 붙이면 상수(constant)가 됩니다. 상수는 선언과 동시에 값을 저장해야하며 한 번 저장된 값은 바꿀 수 없습니다.

대입 연산자는 모든 연산자들 중에서 가장 낮은 연산순위를 가지고 잇기 때문에 제일 마지막에 수행됩니다. 연산진행방향은 오른쪽에서 왼쪽이기 때문에 x=y=3;에서 y=3이 먼저 수행되고 그 다음에 x=y가 수행됩니다.
대입 연산자는 다른 연산자와 결합하여 'op='와 같은 방식으로 사용이 될 수 있습니다.
x = x+7은 x +=7과 같이 표현할 수 있습니다.

Java 연산자(논리연산자)

5. 논리 연산자

5.1 논리 연산자 &&, ll

논리 연산자는 피연산자로 boolean형 또는 boolean형 값을 결과로 하는 조건식만을 허용한다. 조건문과 반복문에서 조건식 간의 결합에 사용합니다.
 그리고 &&가 ll연산보다 우선순위가 높으므로 한 조건식에 &&와 ll가 함께 사용할 떄는 관호를 사용해서 우선순위를 명확하게 해주도록 합니다.

ll(or 결합) - 피연산자 중 어느 한 쪽만 true이면 true를 결과로 얻습니다.
&&(and 결합) - 피연산자 양쪽 모두 true이어야 true를 결과로 얻습니다.

논리 연산자의 또 다른 특징은 효율적인 연산을 한다는 것입니다. or연산 ll의 경우, 두 개의 피연산자 중 어느 한 쪽만 true가 되어도 전체 연산결과가 true가 되므로 좌측의 피연산자가 true면, 우측의 피연사자의 값은 검사하지 않습니다. and연산 &&의 경우도 마찬가지로 어느 한쪽만 false이어도 전체 연산결과가 false이므로 좌측의 피연사자가 false면, 우측의 피연산자의 값은 검사하지 않습니다.
즉 같은 조건식이라도 피연산자의 위치에 따라서 연산속도가 달라질 수 있습니다.

public class Operator23 {

public static void main(String[] args) {

char x = 'e';

if((x>='a' && x<='z') || x>='a' &&  x<='z')
System.out.println("유효한 문자입니다.");
else
System.out.println("유요하지 않은 문자입니다.");


}


}

실행결과

유요하지 않은 문자입니다.

&&와 ||를 조합하여 e가 유요한 영문자인지를 검사하는 조건식을 만들었습니다. 문자형 변수 x에 저장된 문자가 'e'이므로 or연산자의 좌측 피연산자인 조건식(x>='a'&&x<='z')의 결과가 true이기 때문에 나머지 우측 연산자를 검사하지 않고 전체 조건식을 true로 판단합니다.

5.2 비트 연산자 & | ^


비트 연산자는 이진 비트연산을 수행합니다. 값을 이진수로 표현했을 때의 각 자리수의 규칙에 따라 연산을 수행합니다. 실수형인 float와 doulble을 제외한 모든 기본형에서 사용가능합니다.

| (or연산자) : 피연산자 중 한쪽 값이 1이면, 1을 결과로 얻고, 그 외에는 0을 얻습니다.
&(and연산자) : 피연산자 양 쪽이 모두 1이어야 1을 결과로 얻고, 그 외에는 0을 얻습니다.
^(xor연산자) : 피연산자의 값이 서로 다를 때만 1을 결과로 얻고, 그 외에는 0을 얻습니다.


public class Operator23 {

public static void main(String[] args) {
int x = 3;
int y = 7;
System.out.println("x는 "+x+"이고, "+"y는 "+y+"일 때,");
System.out.println("x | y = " +(x|y));
System.out.println("x & y = " +(x&y));
System.out.println("x ^ y = " +(x^y));
System.out.println("true | false= "+(true|false));
System.out.println("true & false= "+(true&false));
System.out.println("true ^ false= "+(true^false));
}

}

실행결과

x는 3이고, y는 7일 때,
x | y = 7
x & y = 3
x ^ y = 4
true | false= true
true & false= false
true ^ false= true

논리 연산자는 덧셈연산자'+'보다 연산우선순위가 낮기 때문에 괄호를 사용해야합니다.



Java 연산자(비교연산자)

4. 비교 연산자

비교 연산자는 두 개의 변수 또는 리터럴을 비교하는데 사용되는 연산자로 주로 조건문과 반복문의 조건식에 사용되며, 연산결과는 true 또는 false입니다.
 비교 연산자 역시 이항 연산자이므로 비교하는 피ㅣ연산자의 자료형이 서로 다를 경우에는 자료형의 범위가 큰 쪽으로 형변환하여 피연산자의 타입을 일치시킨 후에 비교합니다.

4.1 대소비교 연산자 >, <, <=, >=

두 연산자의 크기를 비교하는 연산자이다. 기본형 중에서너는 boolean형을 제외한 나머지 자료형에 다 사용할 수 있지만 참조형은 사용할 수 없습니다.

4.2 등가비교 연산자 ==, !=


두 피연산자에 저장되어 있는 값이 다른지 혹은 다른지를 비교하는 연사자입니다. 대소비교연산자와는 달리, 모든 자료형에 사용할 수 있습니다. 기본형의 경우 변수에 저장되어 있는 값이 같은지를 알 수 있고, 참조형의 경우 객체의 주소값을 저장하기 때문에 두 개의 피연사자가 같은 객체를 가리키고 있는지를 알 수 있습니다.
기본형과 참조형 간에는 서로 형변환이 불가능 하기 때문에 등가비교 연산자(==, !=)의 피연산자로 기본형과 참조형을 함께 사용할 수 없습니다.

public class Operator23 {

public static void main(String[] args) {

if(10 == 10.0f){
System.out.println("10과 10.0은 같습니다.");
}
if('0' != 0){
System.out.println("'0'과 0은 같지 않습니다.");
}
if('A' == 65){
System.out.println("'A'는 65와 같습니다.");
}
int a=5;
if(a>0 && a<10){
System.out.println("5는 0보다 크고 10보다 작습니다.");
}
}

}

실행결과

10과 10.0은 같습니다.
'0'과 0은 같지 않습니다.
'A'는 65와 같습니다.
5는 0보다 크고 10보다 작습니다.

비교 연산자에서도 연산을 수행하기 전에 형변환을 통해 피연산자의 타입을 맞춤 다음 피연산자를 비교합니다. 10==10.0f에서 10은 int이고 10.0f는 float이므로 int형인 10을 float형으로 변환한 다음 비교합니다. 두개의 값이 서로 같으므로 결과로 true를 얻게 되는 것입니다. 또한 'A'==65는 'A'를 int로 변환하여 65의 값을 얻고 65==65을 계산하므로 true를 얻게 됩니다.

public class Operator23 {

public static void main(String[] args) {
float a = 0.1f;
double b = 0.1;
double c = (double)a;
System.out.println("10.0==10.0f ? "+(10.0==10.0f));
System.out.println("0.1==0.1f ? " +(0.1==0.1f));
System.out.println("a="+a);
System.out.println("b="+b);
System.out.println("c="+c);
System.out.println("a==b ? " +(a==b));
System.out.println("b==c ? " +(b==c));
System.out.println("c==a ? " +(c==a));
}

}

실행결과

10.0==10.0f ? true
0.1==0.1f ? false
a=0.1
b=0.1
c=0.10000000149011612
a==b ? false
b==c ? false
c==a ? true

10.0==10.0f는 true인데 0.1==0.1f는 false라는 결과가 나왔다. 정수형과 달리 실수형이 float와 double의 값은 근사값으로 저장되기 때문에 이런 결과가 나오는데, 10.0f을 double로 변환하면 10.0을 얻지만, 0.1f를 double로 변환하면 0.1이 아닌 0.10000000149011612을 얻는다. 이값은 0.1에 상당히 가갑지만 정확한 0.1이 아니기 때문에 false로 출력된다.
이 정도의 오차는 산술계산에서는 무시할 수 있을 정도로 아주 작은 것이지만 값의 크기와 같고 다름을 판단하는 비교연산자에서는 문제가 된다. 따라서 이 문제를 해결하기 위해서 형변환을 통해 두 피연산자의 타입을 flaot로 맞춰 비교연산과정에서 형변환이 일어나지 않도록 하거나, 소수점 몇 째 자리까지 동일하게 잘라서 비교해야합니다.

Java 연산자(산술연산자-쉬프트연산자)

3.3 쉬프트 연산자

쉬프트 연산자는 정수형 변수에만 사용할 수 잇습니다. 피연산자의 각 자리(2진수로 표현했을 때)를 오른쪽 또는 왼쪽으로 이동(shift)한다고 해서 쉬프트 연산자라고 불린다. 오른쪽으로 n자리를 이동하면 피연산자를 2^n로 나눈 것과 같은 결과를, 왼쪽으로 n자리를 이동하면 2^n으로 곱한 것과 같은 결과를 얻습니다.

x << n은 x*2^n의 결과와 같다.
x >> n은 x/2^n의 결과와 같다

<<연산자의 경우, 피연산자의 부호에 상관없이 자리를 왼쪽으로 이동시키며 빈칸을 0으로 채우면 된다. >>연산자의 경우, 오른쪽으로 이동시키기 때문에 음수인 경우 부호를 유지시켜주기 위해서 음수인 경우 빈자리를 1로 채우면 된다. 반면에 >>>연산자의 경우, 부호에 상관없이 항상 0으로 빈자리를 채운다.
곱셈이나 나눗셈 연산자를 사용하면 같은 결과를 얻을 수 잇는데, 굳이 쉬프트 연산자를 제공하는 이유는 속도 때문입니다.
예를 들어 8>>>2의 결과는 8/4의 결과와 같지만, 8/4를 연산하는데 걸리는 시간보다 8>>>2를 연산하는데 걸리는 시간이 더 적게 걸립니다. 즉 << 또는 >>을 사용하는 것이 곱셈과 나눗셈 연산자 보다 더 빠릅니다.

public class Operator22
{

public static void main(String[] args) {

int number;
System.out.println(-8);
//-8진수를 2진수 문자열로 변경합니다.
System.out.println(Integer.toBinaryString(-8));
System.out.println();

number = -8 << 1;
System.out.println("-8 << 1 =" +number);
System.out.println(Integer.toBinaryString(number));
System.out.println();

number = -8 << 2;
System.out.println("-8 << 2 =" +number);
System.out.println(Integer.toBinaryString(number));
System.out.println();

System.out.println();
number = -8;
System.out.println(Integer.toBinaryString(number));
System.out.println();

number = -8 >> 1;
System.out.println("-8 >> 1 =" +number);
System.out.println(Integer.toBinaryString(number));
System.out.println();

number = -8 >> 2;
System.out.println("-8 << 1 =" +number);
System.out.println(Integer.toBinaryString(number));
System.out.println();

System.out.println();
number = -8;
System.out.println(Integer.toBinaryString(number));
System.out.println();

number = -8 >>> 1;
System.out.println("-8 >>> 1 =" +number);
System.out.println(Integer.toBinaryString(number));
System.out.println();

number = -8 >>> 2;
System.out.println("-8 >>> 2 =" +number);
System.out.println(Integer.toBinaryString(number));
System.out.println();

}
}

실행결과

-8
11111111111111111111111111111000

-8 << 1 =-16
11111111111111111111111111110000

-8 << 2 =-32
11111111111111111111111111100000


11111111111111111111111111111000

-8 >> 1 =-4
11111111111111111111111111111100

-8 << 1 =-2
11111111111111111111111111111110


11111111111111111111111111111000

-8 >>> 1 =2147483644
1111111111111111111111111111100

-8 >>> 2 =1073741822
111111111111111111111111111110

-8>>>1과 -8>>>2의 결과에서 맨 앞의 0은 생략된 것이다. Integer.toBinaryString(int i)는 정수 2진수 문자열로 변환할 때 앞부분의 0은 생락합니다.

2014년 9월 3일 수요일

Java 연산자(산술연산자-나머지연산자)

3.2 나머지 연산자

왼쪽의 피연산자를 오늘쪽 피연산자로 나누고 난 나머지 값을 돌려주는 연산자입니다.
boolean형을 제외하고는 모든 기본형 변수에 사용할 수 있습니다. 나머지 연산자는 주로 짝수, 홀수 또는 배수 검사 등에 주로 사용이 됩니다.
 나눗셈에서와 같이 피연산자가 정수형인 연산에서는 나누는 수(오른쪽 피연산자)로 0을 사용할 수 없고, 나머지 연산자 역시 0.0이나 0.0f로 나누는 것은 허용합니다.

public class Operator19
{

public static void main(String[] args) {

int portion = 10/8;
int remain = 10%8;
System.out.println("10을 8로 나누면,");
System.out.println("몫은 "+portion+"이고, 나머지는 " +remain+"입니다");

}
}

실행결과

10을 8로 나누면,
몫은 1이고, 나머지는 2입니다

public class Operator1 
{

public static void main(String[] args) {
for(int i=1; i<=10; i++){
if(i%3==0){
System.out.println(i);
}
}
}
}
실행결과

3
6
9

조건문과 반복문을 사용해서, 1과 10사이의 정수에서 3의 배수인 숫자만 출력하는 예제입니다. 반복문 for에서 i값을 1부터 10까지 1씩 증가시키면서 괄호{} 안의 문장들을 반복해서 수행합니다. 조건문 if는 조건이 만족하는 경우만 괄호{}안의 문장들을 수행합니다.  그래서 i%3의 결과가 0인 경우에만 i의 값을 화면에 출력합니다.

public class Operator20
{

public static void main(String[] args) {
System.out.println(-10%8);
System.out.println(10%-8);
System.out.println(-10%-8);
}
}

실행결과

-2
2
-2

피연산자 중에 음의 부호가 있는 경우에 어떤 결과를 얻는지 보여주는 예제이다. %연산자의 왼쪽에 있는 피연산자(나누어지는 수)의 부호에 따라 결정된다.
 피연산자(나누는 수)의 부호를 모두 무시하고 나머지 연산을 한 결과에 나눠지는 수의 부호를 붙이면 된다.




Java 연산자(산술연산자-사칙연산자)

3. 산술연산자

산술 연산자인 사칙 연산자(+ - * /), 나머지 연산자(%), 쉬프트 연산자(<<,>>,>>>)는 모두 두개의 피연산자를 취하는 이향 연산자입니다.
모든 이항 연산자는 연살을 수행하기 전에
 - 크기가 4byte이하인 자료형을 int형으로 변환한다.(byte, char, short -> int)
- 피연산자들의 타입을 서로 일치시킵니다.

3.1 사칙 연산자 + - * /

사칙 연산자는 프로그래밍에서 가장 많이 쓰이는 연사자 입니다. 보통 사람들이 이미 알고 있듯이 곱셈(*),나눗셈(/),나머지(%) 연산자가 덧셈(+),뺄셈(-)연산자 보다 우선수위가 높습니다.
1. int형(4byte)보다 크기가 작은 자료형은 int형으로 형변환 후에 연산을 수행합니다.
    byte + short -> int + int -> int
2. 두개의 피연산자 중 자료형의 표현범위가 큰 쪽에 맞춰서 형변환 후 수행합니다.
    int + float -> float + float -> float
3. 정수형 간의 나눗셈에서 0으로 나누는 것은 금지되어 있습니다.


ex)
byte + byte = int + int -> int
byte + short = int + int -> int
char + char = int + int -> int

float + int -> float + float -> float
long + float -> float + float -> float
float + double -> double + double -> double 

피연산자가 정수형인 경우, 나누는 수로 0을 사용할 수 없습니다. 만일 0으로 나누면, 컴파일은 정상적으로 되지만 실행 시 오류가 발생합니다. 하지만 부동소수점값인 0.0f, 0.0d로 나누는 것은 가능하지만 그 결과는 NaN(Not A Number, 숫자 아님)입니다.


public class Operator8 {

public static void main(String[] args) {
byte a = 10;
byte b = 20;
byte c = (byte) (a + b);
System.out.println(c);
}
}

실행결과


30

7번째 줄을 보면 형변환을 하였는데, a와 b는 int형보다 작은 byte형이기 때문에 연산자'+'는 이 두 개의 피연산자들의 자료형을 int형으로 변환한 다음 덧셈을 수행합니다. 그래서 'a+b'의 연산결과는 byte형이 아닌 int형이기 때문에 7번째 줄에 형변환을 꼭 붙여야 합니다. 그렇지 않으면 에러가 발생합니다.
크기가 작은 자료형의 값을 큰 자료형의 변수에 저장할 때는 자동으로 형변환되지만, 큰 자료형의 값을 작은 자료형 변수에 저장하려면 캐스트 연산자를 사용해서 변환해주어야 한다.
(주의) byte c = (byte)a+b;와 같이 문장을 써도 에러가 발생합니다. 그 이유는 형변환 연산자는 단항 연산자이므로 연산 순위가 이항 연산자인 덧셈 연산자보다 높습니다. 그렇기 때문에
(byte)a가 먼저 수행되고 다음 덧셈 연산이 수행됩니다. 즉 a만 byte형으로 형변환된 후 int형인 b와 연산을 합니다.

public class Operator9 {

public static void main(String[] args) {
byte a = 30;
byte b = 10;
byte c = (byte) (a*b);
System.out.println(c);
}
}

실행결과

44

이 예제를 실행하면 44가 화면에 출력되는데, 연산결과는 300이지만, 앞의 형변환에서 배웠듯이 큰 자료형에서 작은 자료형으로의 변환을 하면 데이터 손실이 발생하므로 원하는 결과값이 안나옵니다. 300은 byte의 범위를 넘기 때문에 byte형으로 변환하면 데이터 손실이 발생하여 44가 출력이 됩니다. byte형의 범위인 -128~127의 범위를 넘는 int형의 값을 byte형으로 변환하면, 원래의 값은 손실이 되고, byte형의 범위 중 한 값을 가지게 됩니다. 따라서 원하는 결과 값을 얻기 위해서는 충분히 큰 자료형을 사용해야합니다.

public class Operator10 {

public static void main(String[] args) {

int a = 1000000;
int b = 1000000;
long c = a * b;
System.out.println(c);


}
}

실행결과

-727379968

a*b의 결과를 long형 변수c에 저장하기 때문에 2*10^12를 저장하기에 충분하므로 '20000000000000'이 출력될 것 같지만, 결과는 예상과 다르게 나왔습니다. 이유는 int형과 int형의 연산결과는 int형이기 때문입니다. a*b의 결과가 이미 int형 값(-727379968)이기 때문에 long형으로 자동 변환이 되어서 long형 변수인 c에 저장되어도 결과는 같습니다. 따라서 원하는 값을 얻기 위해서는 변수 a, b의 타입을 long으로 바꾸어야 합니다.

public class Operator11 {

public static void main(String[] args) {

long a = 1000000 * 1000000;
long b = 1000000 * 1000000L;

System.out.println(a);
System.out.println(b);

}
}

실행결과

-727379968
1000000000000

1000000 * 1000000은 int와 int의 연산이기 때문에 그 결과가 int입니다. 그래서 10^6*10^6의 곱셈연산은 10^12이기 때문에 int의 최대값인 대략 2*10^9을 넘기 때문에 오버플로우가 발생해서 -727379968를 결과로 얻습니다.
1000000 * 1000000L은 int와 long의 연산이기 때문에 그 결과가 long입니다. long은 10^12을 저장하기에는 충분한 자료형이므로 원하는 결과를 얻을 수 있습니다.


public class Operator12 {

public static void main(String[] args) {

long a = 1000000 * 1000000 / 1000000;
long b = 1000000 / 1000000 * 1000000;

System.out.println(a);
System.out.println(b);

}
}

실행결과

-727
1000000

곱셉 연산을 먼저 하면 int의 범위를 넘어서기 때문에 워하는 결과가 나오지 않습니다.
위와 같이 곱셈과 나눗셈의 연산 중에 무엇을 먼저 쓰느냐에 따라서 결과가 달르게 나옵니다. 

public class Operator13 {

public static void main(String[] args) {

char ch1 = 'a';
char ch2 = ch1;
char ch3 = ' ';

int i = ch1 + 1;

ch3 = (char)(ch1+1);
ch2++;
ch2++;

System.out.println("i="+i);
System.out.println("ch2="+ch2);
System.out.println("ch3="+ch3);

}
}

실행결과

i=98
ch2=c
ch3=b

ch1을 계산할 때, ch1이 char형이므로 int형으로 변환한 후 덧셈연산을 수행하게 되기 때문에 c1에 저장되어 잇는 코드값이 변환되어 int형 값이 된다. 따라서 ch1+1은 97+1이 되어 int형 변수 i에는 98이 저장이 됩니다.
ch2++은 형변환없이 ch2에 저장되어 있는 값을 1 증가시키므로, 예제에서는 원래 저장되어 있던 값인 97을 1씩 두 번 증가하여 99가 됩니다. 코드값이 10진수로 99인 문자는 'c'이므로 c2를 출력하면 c가 출력됩니다.


public class Operator14 {

public static void main(String[] args) {


char ch1 = 'a';
// char ch2 = ch1+a;  //컴파일 에러 발생
char ch2 = 'a'+1;       //컴파일 에러 없음

System.out.println(ch2);

}
}

실행결과

b

덧셈 연사자와 같은 이항 연산자는 int보다 작은 타입의 피연사자를 int로 자동형변환한도 했는데 왜 에러가 발생하지 않는 것일까?
'a+1'이 리터널 간의 연산이기 때문이다. 상수 또는 리터널 간의 연산은 실행과정동안 변한느 값이 아니기 때문에, 컴파일 할때 컴파일러가 계산해서 그 결과로 대체함으로써 코드를 보다 효율적으로 만든다.
컴파일러가 미리 덧셈연산을 수행하기 때문에 실행 시에는 덧셈 연산이 수행되징 ㅏㄶ는다. 그저 덧셈연산결과인 문자 b를 ch2에 저장한다.

컴파일 전의 코드               컴파일 후의 코드
char ch2 = 'a'+1'              char ch2 = 'b';
int a = 60*60*60;              int a = 216000;

하지만 에러가 발생하는 부분은 수식에 변수가 들어가 있다. 변수가 들어가 있는 경우에는 컴파일러가 미리 계산을 할 수 없기 때문에 아래의 오른쪽 코드와 같이 형변화을 해주어야 한다.
즉 char ch2 = c1 + 1; -> char ch2 = (char)(c1 + 1);

public class Operator15 {

public static void main(String[] args) {

char ch = 'a';
for(int i=0; i<26; i++){       //출력() 안의 문장을 26번 반복한다.
System.out.print(ch++);   //'a'부터 26개의 문자를 출력한다.
}
System.out.println();         //줄바꿈
ch = 'A';
for(int i=0;i<26;i++){
System.out.print(ch++);
}
System.out.println(); 
ch = '0';
for(int i=0;i<10;i++){
System.out.print(ch++);
}
System.out.println(); 
}
}

실행결과

abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789

문자 a부터 z까지 26개의 문자를 출력하고, 문자A부터 Z까지 26개의 문자를 출력하며, 0부터 9까지 10개의 숫자를 출력합니다. 문자 a의 코드값은 10진수로 97, b의 코드값은 98.... z의 코드값은 122입니다. 문자 A의 코드값은 10진수로 65...Z의 코드값은 90입니다. 그리고 문자 0의 코드값은 48입니다.
이를 이용하여 대문자를 소문자로, 소문자를 대문자로 변환하는 프로그램을 작성할 수 있습니다.

public class Operator1 {

public static void main(String[] args) {
char ch1 = 'a';
char ch2 = (char)(ch1 - 32);
System.out.println(ch2);
}
}

실행결과

A


a의 코드값이 97이고, A의 코드값이 65입니다. a보다 32가 적으므로 소문자 a의 코드값에서 32를 빼면 대문자 A가 출력되고, 반대로 A의 코드값에 32를 더하면 소문자 a가 출력이 됩니다.

public class Operator18 
{

public static void main(String[] args) {
float Pi = 3.141592f;
float intPi = (int)(Pi*1000)/1000f;
System.out.println(intPi);
}
}

실행결과

3.141

int형 간의 나눗셈 int/int를 수행하면 float나 double이 아닌 int다. 그리고 나눗셈의 결과는 반올림이 아니라 버린다. 1/2의 결과는 1.5가 아니라 1이다.
위의 예제는 누눗셈 연산자의 성지을 이용해서 실수형 변수 pi의 값을 소수점을 셋째자리가지만 빼내는 방법을 보여준다.
(int)(pi * 1000)/1000f;에서 (pi*1000)이 먼저 수행이 된다. pi는 float, 1000은 정수형이기 때문에 연산 결과는 float형인 3.141.592f가 됩니다.
(int)(3141.592f)/1000f에서 단항연산자인 캐스트연산자의 형변환이 수행됩니다. 3141.592f를 int로 변환하면 3141을 얻게 됩니다. 소수점 이하의 자리는 반올림 없이 버려집니다.
3141/1000f;에서 int와 float의 연산이므로 int가 float로 변환된 다음, float/float의 연산이 수행됩니다. float와 float의 연산이므로, float인 3.141f가 출력됩니다.






Java 연산자(비트전환 연산자와 논리부정 연산자)

2.3 비트전환 연산자 ~


비트전환 연산자'~'는 정수형과 char형에서만 사용이 되며, 피연산자를 2진수로 표현 했을 때, 0은 1로 1은 0으로 바꾼다. 그래서 연산자 '~'에 의해 비트전환이 되면 피연산자의 부호가 반대로 변경된다.
(byte, short, char형은 int형으로 변경된 후 전환된다)

2진수                  10진수
0/0/0/0/0/0/0/0      +10
1/1/1/1/1/1/1/1      -11

public class operarot5 {

public static void main(String[] args) {

byte b = 10;
System.out.println("b="+b);
System.out.println("~b="+~b);
System.out.println("~b+1="+(~b+1));
}

}

실행결과

b=10
~b=-11
~b+1=-10

결과를 보면, 어떤 양의 정수에 대한 음의 정수를 얻으려면 어떻게 해야하는지 알 수 있습니다. 양의 정수 b가 있을 때, b에 대한 음의 정수를 얻으려면 ~b+1을 계산하면 됩니다.

      2진수                     10진수         
0/0/0/0/1/0/1/0                 10
1/1/1/1/0/1/0/1                -11
1/1/1/1/0/1/0/1                -11
0/0/0/0/0/0/0/1          +)     1
1/1/1/1/0/1/1/0                -10

먼저 10을 2진수로 표현한 다음 0을 1로, 1을 0으로 바꾸고 그 결과에 1을 더하면 -10의 2진 표현을 얻을 수 있다.


public class Operator6 {

public static void main(String[] args) {
byte a = 10;
byte result = (byte) ~a;
System.out.println("a = "+a);
System.out.println("result = "+result);
}
}

실행결과


a = 10
result = -11

연산자 ~는 피연산자 타입이 int보다 작으면, int형으로 변환 후 연산을 수행하기 때문에 위의 예제에서 byte형 변수 a를 int형으로 변환 후 연산을 수행하기 때문에 연산결과를 저장하기 위해서는 int형 변수에 저장하거나, 캐스트 연산자를 사용해야합니다.

2.4 논리부정 연산자 !

이 연산자는 boolean형에만 사용할 수 있으며, true는 false로 false는 true로 변경합니다.
존건문과 반복문의 조건식에 사용되어 조건식을 보다 효율적으로 만들어 줍니다. 연산자 !를 이용해서 한번 누르면 on, 또 한번 누르면 off가 되는 토글버튼을 논리적으로 구현할 수 있습니다.

public class Operator7 {

public static void main(String[] args) {
boolean power = true;
System.out.println(power);
power = !power;
System.out.println(power);
power = !power;
System.out.println(power);
}
}

실행결과

true
false
true


2014년 9월 2일 화요일

Java 연산자(증감연산자와 바호연산자)

1. 연산자(operator)

연산자는 프로그래밍언어에서 가장 기본적이면서도 중요한 요소이다. 각 연산자의 특징과 수행결과, 그리고 우선순위에 대해서 잘 알아야 합니다.

종 류
연산방향
연산자
우선순위
단항 연산자
++  -- + - ~ ! (타입)
높음
산술 연산자
X / %
 
+ -
 
<< >> >>>
 
비교 연산자
> < >= >==
 
== !=
 
논리 연산자
&
 
^
 
l
 
&&
 
ll
 
삼항 연산자
?:
 
대입 연산자
= *= /= %= += -= <<= >>= >>>= &= ^= l=


표에서의 (타입)은 형변환에 사용되는 캐스트 연산자 입니다.
위의 표에서 같은 줄에 있는 연산자들은 우선순위가 같습니다. 우선순위가 같은 연산자들 간에는 연산의 진행방향에 의해서 연산순서가 정해집니다.
 연산의 진행방향을 설명하기 위해 두 개의 식을 예로 보여드리 겠습니다.

                           3*4*5                                                   x=y=3
       연산방향이 왼쪽에서 오른쪽인 경우        연산방향이 왼쪽에서 오른쪽인 경우

연산방향이 왼쪽에서 오른족인 곱센연산자의 경우 3*4를 수행하여 12의 값을 얻고 그 다음 12*5를 수행합니다. 이와 반대로 연산방향이 오른쪽에서 왼쪽인 대입연산자의 경우 y=3을 먼저 수행하여 Y에 3이 저장이 된 후 x=y가 수행되어 y에 저장되어 있는 값이 x에 저장되어 x와 y는 같은 값을 갖게 된다. 이는 x=3, y=3을 한 문장으로 쓴 것이다.
 연산자의 우선순의는 보통 사람들이 다 알고 있는 내용이기에 한번 보고 우선순위를 보고 넘어가자. 

산술 > 비교 > 논리 > 대입. 대입은 제일 마지막에 수행된다.
단항(1) > 이항(2) > 삼항(3). 단항 연산자의 우선쉰위가 이항 연산자보다 높다.
단항 연산자와 대입 연사자를 제외한 모든 연산의 진행방향은 왼쪽에서 오른쪽이다.

2.단항 연산자

2.1 증감 연산자

일반적으로 단항 연산자는 피연산자의 왼쪽에 위치하지만, 증가 연산자 ++와 감소 연산자--는 양쪽 모두 가능합니다. 하지만 연산자의 위치의 따라 연산결과가 달라 질 수도 있습니다.

증가 연산자(++) : 피연사자의 값을 1 증가시킵니다.
감소 연산자(--) : 피연산자의 값을 1 감소시킵니다.

boolean형을 제외한 모든 기본형(Primitive type) 변수에 사용이 가능하며, 피연산자의 왼쪽에 사용하는 전위형과 오른쪽에 위치하는 후위형이 있습니다.

증가 연산자의 후위,전위형 예제

public class Operator1 {

public static void main(String[] args) {

int i = 5;
i++;
System.out.println(i);
i = 5;
++i;
System.out.println(i);
}

}

실행결과
6
6

i를 증가시켜 출력하는데 한번은 후위형(i++) 그리고 전위형(++i)을 이용했습니다. 결과는 모두 6으로 같습니다. 그 이유는 단독적으로 사용했기 때문에, 증가연산자(++)를 전,후에 상관없이 값이 같습니다.

증가 연산자의 후위,전위형의 또다른 예제

public class Operator2 {

public static void main(String[] args) {
int a=5;
int b=0;
b = a++;
System.out.println("b=a++;를 실행 후, a="+ a +", b ="+b);
a=5;
b=0;
b = ++a;
System.out.println("b=++a;를 실행 후, a="+ a +", b ="+b);
}

}

실행결과

b=a++;를 실행 후, a=6, b =5
b=++a;를 실행 후, a=6, b =6

b의 값은 1이 증가되어 6이 저장되는 것은 같지만, 실행 결과가 다르다는 것을 보여줍니다. 수식을 계산하기 위해서는 수식에 포함된 변수의 값을 가지고 오는데, 전위형은 변수의 값을 먼저 증가시킨 후에 변수의 값을 읽어오는데, 후위형은 변수의 값을 먼저 읽어온 후에 값을 증가시킵니다. 따라서 b=++a;(전위형)에서는 a의 값을 증가시킨 후에 값을 읽어오고, b=a++;(후위형)에서는 a의 값을 읽어오고 a의 값을 증가시키기 때문에 결과 값이 다르게 나옵니다.

전위형   b=++a;   ++a;,a=b;  값이 참조되기 전에 증가시킵니다.
후위형   b=a++;   a=b;,a++;  값이 참조된 후에 증가시킵니다.   

하지만 ++a;,와 a++; 같이 증감연산자를 단독으로 쓰는 경우에는 차이가 없습니다.

증감연산자가 함수의 매개변수에 사용된 예제

public class Operator3 {

public static void main(String[] args) {
int a=5, b=5;
System.out.println(a++);
System.out.println(++b);
System.out.println("a = "+a+", b = " +b);
}

}

실행결과

5
6
a = 6, b = 6

a의 값이 증가 되기전에 참조되므로 println메소드에 i에 저장된 값 5를 넘기고, a의 값을 증가시키기 때문에 5가 출력이되고, b는 b에 저장된 값을 증가 시킨 후에 값을 넘겨주므로 6이 출력된다. 하지만 결과적으로는 a,b둘다 6이 저장이 된다.
감소연산자(--)는 덧셈연산자(++)와 같은 원리로 작동되기 때문에 설명하지 않겠습니다.

System.out.println(a++);                    system.our.println(a);
System.out.println(++b);                    a++;                         
                                                       ++b;                         
                                                       System.out.println(b);

2.2 부호 연산자 -  +

부호 연산자는 피연산자의 부호를 변경하는데 사용이 되며, boolean형과 char형을 제외한 나머지 기본형에서 사용할 수 있습니다.
 부호 연산자 '+'는 피연산자에 양수 1을 곱한 것이고, '-'는 피연산자에 음수 1을 곱한 것이다.

부호연산자의 예제

public class Operator1 {

public static void main(String[] args) {
int i = -10;
i = +i;
System.out.println(i);
i= -10;
i= -i;
System.out.println(i);
}

}

실행결과

-10
10

Java 변수(6-형변환)

3. 형변환

3.1 형변환(casting)이란?

모든 리터럴과 변수에는 타입이 있습니다. 프로그램을 작성하다 보면, 서로 다른 타입의 값으로 연산을 수행해야하는 경우가 생기게 됩니다.
 모든 연산은 기본적으로 같은 타입의 피연산자간에만 수행될 수 있으므로, 서로 다른 타입의 피연산자간의 연산을 수행해야하는 경우, 연산을 수행하기 전에 형변환을 통해 같은 타입으로 변환해 주어야 합니다.
형변환이란, 변수 또는 리터럴의 타입을 다른 타입으로 변환하는 것입니다.

3.2 형변환의 방법

기본형과 참조형 모두 형변환이 가능하지만, 기본형과 참조형 사이에는 형변환이 성립되지 않습니다. 기본형은 기본형으로만 참조형은 참조형으로만 형변환이 가능합니다.
 형변환 방법은 형변환하고자 하는 변수나 리터럴 앞에 변환하고자하는 타입을 괄호와 함께 붙여 주면 됩니다.

int score = (int)85.4; // double형 값을 int형으로 변환하여 score에 85를 저장합니다.          
byte b = (byte)score; //score에 저장된 값을 byte형으로 변환하여 byte에 저장합니다.        

피연산자 앞에 변환하고자 하는 타입의 이름을 괄호에 넣어 붙여주기만 하면 됩니다. 형변환에서 사용되는 괄호는 '케스트 연산자'또는 '형변환 연산자'라고 하며, 형변환을 '캐스팅(casting)'이라고 합니다.
캐스트 연산자는 피연산자의 값을 읽어서 지정된 타입으로 형변환하고 그 결과를 반환한다. 그래서 피연산자는 형변환 후에도 아무런 변환가 없습니다.

캐스팅 변환예제

public class Casting1 {

public static void main(String[] args) {
double d= 100.0;
int i = 100;
int result = i + (int)d;
System.out.println("i="+i);
System.out.println("d="+d);
System.out.println("result="+result);

}

}

실행결과
i=100
d=100.0 <- 형변환 이후에 아무런 변화가 없습니다.
result=200

double형 변수 d와 int형 변수 i의 덧셈연산을 하기 위해 캐스트 연산자를 이용해서 변수 d를 int형으로 변환하여 덧셈연산을 합니다. 그리고 그 결과를 int형과 int형의 연산결과는 항상 int형의 값을 결과로 얻으므로 result타입을 int로 하였습니다.
 위의 예제를 보면 d=100.0으로 형변환 후에도 변수 d에 저장된 값에는 변함이 없습니다.

3.3 기본형의 형변환

8개의 기본형 중에서 boolean을 제외한 나머지 7개의 기본형 간에는 서로 형변환이 가능합니다.
                                             형변환           수식          결과  
 int -> char     (char)65         'A'   
char -> int      (int)'A'           65    
float -> int      (int)1.6f          1     
   int -> float     (float)10       10.0f

각 자료형 마다 표현할 수 있는 값의 범위가 다르기 때문에 범위가 큰 자료형에서 범위가 작은 자료형으로의 변환은 값 손실이 발생할 수 있다.
예를 들어 실수형을 정수형으로 변환하는 경우 소수점 이하의 값은 버려지게 된다. 위에서의 예와 같이 float형 1.6f를 int로 변환하면 1이 된다.
반대로 범위가 작은 자료형에서 큰 자료형으로 변환하는 경우에는 절대로 값 손실이 발생하지 않으므로 변환에 아무런 문제가 없다.

int형과 byte간의 형변환 예제
public class Casting2 {

public static void main(String[] args) {
byte a = 10;
int i = (int)a;
System.out.println("a="+a);
System.out.println("i="+i);
int i2=100;
byte a2=(byte)i2;
System.out.println("i2="+i2);
System.out.println("a2="+a2);

}

실행결과
a=10
i=10
i2=100
a2=100

byte형을 int형으로, int형을 byte형으로 변환하고 그 결과를 출력하는 예제입니다.
 byte과 int 모두 정수형으로 크기는 1byte와 4byte이며, 표현할 수 있는 값의 범위는 byte가 -2^7~2^7-1(-128~127), int가 -2^31~2^31-1입니다. byte의 범위를 int가 포함하고 있으며, int가 훨씬 큰 표현 범위를 갖고 있습니다. 

byte -> int                                                                                                                   
                                         /00001010(byte)                                                                   
00000000/00000000/00000000/00001010(int)         10진수        값손실 없음                          

int -> byte                                                                                                                   
00000000/00000000/00000000/00001010(int)                                                                      
                                         /00001010(byte)      10진수        값손실 있음                          

위의 그림과 같이 byte값을 int값으로 변환하는 것은 1byte에서 4byte로 나머지 3byte(24자리)를 단순히 0으로 채워주면 되므로 기존의 값이 그대로 보존이 됩니다. 하지만 int값을 byte값으로 변환하는 것은 1byte에서 4byte로 나머지3 byte(24)를 잘라내서 1byte로 만드는 것이므로 기존의 int값이 보존될 수도 없을 수도 있습니다.

1byte         2byte        4byte      8byte        4byte          8byte                                          
 byte   ->   short   ->   int   ->   long   ->   float   ->   double                                         
                  char   ->   int                                                                                             

현변환이 가능한 7개의 기본형을 왼쪽부터 오른쪽으로 표현할 수 있는 값의 범위가 작은 것부터 큰 것의 순서로 나열한 것이다. 왼쪽에서 오른쪽으로의 변환은 캐스트 연산자를 사용하지 않아도 자동형변환이 되며, 그 반대 방향으로의 변환은 반드시 캐스트 연산자를 이용한 형변환을 해야합니다.
 보통 자료형의 크기가 클 수록 값의 표현범위가 크지만, 실수형은 정수형과 다르게 값을 표현하기 때문에 같은 크기형이라도 정수형보다 실수형이 훨씬 더 큰 표현범위를 갖습니다. 따라서 float와 double형이 int와 long형보다 오른족에 위치합니다.
 char와 short는 둘 다 2byte의 크기로 크기가 같지만 char형의 범위는 0~2^16-1(0~65535)이고 short의 범위는 -2^15~2^15-1(-32768~32767)이므로 서로 범위가 달라 둘 중 어느쪽으로 형변환을 하여도 값 손실이 발생할 수 있으므로, 자동적으로 형변환이 수행되지 않습니다.

boolean형을 제외한 나머지 기본형은 서로 형변환이 가능합니다.
기본형과 참조형은 서로 형변환을 할 수 없습니다.
서로 다른 타입의 변수간의 연산은 형변환을 하는 것이 원칙이지만, 값의 범위가 작은 타입에서 큰 타입으로의 형변환은 생략할 수 있습니다.

2014년 9월 1일 월요일

Java 변수(5-정수형과 실수형)

2.4 정수형 - byte, short, int, long


정수형에는 모두 4개의 자료형이 있으며 저장할 수 잇는 값의 범위가 서로 다르다 크기순으로 나열하면 다음과 같습니다. 크기는 byte입니다.(1byte = 8bit)

byte(1) < short(2) < int(4) < long(8)

byte부터 long까지 2배씩 증가한다. 이중에서 기본 자료형(default data type)은 int이다.
변수에 저장하려는 정수값의 범위에 따라 선택하면 된다. byte나 short보다는 int를 사용하는 것이 좋다.
 정수형 변수의 선언과 초기화는 다음과 같습니다.

byte b = 1;                                                                                                                   
short s = 2;                                                                                                                  
int c = 10                                                                                                                     
long d = 1000000000L(long 타입의 리터럴에는 접미사 L을 붙여야 한다.)                            

위의 문장은 10진수를 변수에 저장한 것이다.
 16진수 혹은 8진수를 표현된 정수 또한 변수에 저장할 수 있는데, 16진수라는 것을 알려주기 위하여 리터널 앞에 접두사'0x' 또는 'OX'를, 8진수의 경우에는 '0'을 붙인다.

int OctNumber = 010;  //8진수 10, 10진수로는 8                                                               
int HexNumber = 0x10; // 16진수 10, 10진수로는 16                                                         

정수형은 0과 1로 이루어진 2진수로 저징이 됩니다. 가장 쉬운 예로 byte의 경우 크기가 1byte이고 1byte는 8bit이므로 byte형은 0/0/0/0/0/0/0/0이 됩니다. 각 자리에는 0또는 1만이 허용이 되기 때문에 1byte로 2^8이 됩니다.(256가지의 값을 표현 할 수 있습니다.)
 모든 정수형은 0을 포함한 양수와 음수를 저장하는 값의 범위로 하기 때문에, 8자리 중에서 왼쪽에서 첫 번째 자리를 부호자리로 사용하기 때문에 실제 표현하는 값의 자리수는 7개, 즉 ±2^7개가 되는 것이다. 따라서 byte형 변수가 가질 수 있는 값의 범위는 -128~`127(-2^7~2^7-1)이 된다.

정수형의 범위한계에 대한 예제

package overflow;

public class overflow {

public static void main(String[] args) {
byte a = 0; //byte형 변수 b를 선언하고 0으로 초기화
int b = 0; //int형 변수를 i로 선언하고 0으로 초기화

for(int x=0;x<=270;x++){ //x가 0에서 시작하여 270까지 증가하는 반복문
System.out.println(a++); //a를 하나씩 증가시키면서 출력
System.out.print('\t'); //줄 바꿈이 아니라 tap
System.out.println(b++); //b를 하나씩 증가시키면서 출력
}

}

}

실행결과
0         0                               -3      253
1         1                               -2      254
2         2                               -1      255
....                                         0      256
125     125                              1      257
126     126                              2      258 
127     127                              .....
-128     128                            12     268
-127     129                            13     269
....                                        14     270

위에 보는 것과 같이 byte형으로 표현할 수 잇는 값의 범위는 -128~127(-2^7~2^7-1)이다. 따라서 위의 예제를 보면 byte와 int가 0으로 시작하여 1씩 증가를 하여 270까지 가야하는 코드이지만 byte의 경우는 저장 범위를 넘어섰기 때문에 오버플로우(overflow)가 발생하게 된다. 오류는 없지만 예상한 결과가 나오지 않게 된다는 것이다. 따라서 원하는 결과를 얻기 위해서는 정수형의 저장할 수 있는 범위를 고려하여 선택해야 한다. 
어떤 기계판이 있다고 하자, 그 기계판이 세 자리 숫자로 표현이 된다고 하자, 그리고 000부터 999까지 밖에 표현하지 못하므로 999가 넘어가면 다시 000으로 돌아오게 된다. byte의 경우 최소 -128(최소값)에서 시작하여 127(최대값)을 넘어가면 다시 -127부터 시작한다. 즉, 256을 주기로 값이 계속 반복된다. 또 다른 정수형 타입인 short, int, long 역시 저장할 수 있는 값의 범위를 넘는 경우 위의 예제와 같이 된다. 변수의 범위를 넘는 값으로 초기화하는 것은 허용하지 않는다.(byte a = 128; // 에러가 발생합니다. 즉 초과 범위 설정안 안됩니다.)

2.5 실수형 - float, double

실수형은 크게 float와 double, 두 가지가 있으며 실수를 저장하는데 사용이 됩니다. float는 4byte 그리고 double은 8byte로 정수형 int와 long형과 같지만 다른 저장 방식 때문에 정수형 보다 저장범위가 훨씬 더 큰 범위의 값을 표현할 수 있습니다.
 실수는 정수와 달리 부동소수점(floating-point) 방식으로 저장을 하는데 부동소수점 방식은 실수를 ±a*10^n의 형태로 표현합니다. a는 가수이고 n은 지수입니다. 단 가수 a는 0=<a<1 범위의 실수이어야 합니다.
예를들어 3.14를 부동소수점 방식으로 표현하면 0.314*10^1이며 가수는 0.314이고 지수는 1이 됩니다.

float : 1 + 8 + 23 = 32bit = 42bit
S(1)      E(8)        M(23)

double : 1 + 11 + 52 = 64bit = 8byte
S(1)      E(11)       M(52)

(숫자)는 할당된 자리수 입니다. 가수(M)를 표현하는데 있어서 float에 비해 double이 약 두 배의 자리수가 배정되어 있기 때문에 double이 float보다 정밀도(소수점 이하의 자리수)가 더 높은 값을 표현할 수 있다는 것을 알 수 있습니다.
float는 가수를 10진수로 7자리 정보를 표현할 수 있기 때문에 더 높은 정밀도를 요구하는 작업을 할 때는 double을 사용해야 합니다.

float와 double의 정밀도에 관한 예제

public class Presicion {

public static void main(String[] args) {

float f = 1.2345678901234567890f;
double d = 1.2345678901234567890;
float f2 = 0.100000001f;
double d2 = 0.100000001;
double d3 = 0.1000000000000001;

System.out.println(f);
System.out.println(d);
System.out.println(f2);
System.out.println(d2);
System.out.println(d3);

}

}

실행결과

1.2345679 <- 끝자리가 반올림되었다.
1.2345678901234567
0.1 <- 0.100000001f
0.100000001
0.1000000000000001

float형 변수 f2에 0.100000001f를 저장하였는데도 0.1이 출력되는 이유는 float가 표현할 수 잇는 가수의 자리수(정밀도)를 넘어서는 것이기 때문입니다. 같은 값을 저장한 double형 변수 d2는 가수의 자리수 float보다 약 2배정도 많기 때문에 값을 더 정확히 표현할 수 있습니다.
 float형 리터럴에는 접미사 f가 사용되고 double형 리터널에는 d가 사용이 됩니다.
정수형에는 int가 기본 자료형인 것 처럼 실수형에서는 double이 기본 자료형이다 그래서 접미사를 생략하면 double형 리터널로 간주한다.
float pi = 3.14                                                                                                               
위와 같이 문장을 써버리면 실수형에서는 double이 기본 자료형이기 때문에 float접미사를 생략하면 double형 리터럴로 간주되기 때문에 오류가 난다.