Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

원문 : http://chortle.ccsu.edu/java5/Notes/chap11/ch11_2.html 자바는 floatdouble의 원시데이터타입을 포함하고 있다. float 는 소숫점을 포함하는 숫자다. 예를들자면 3.14159-0.718802와 같은 숫자들이다.

이번장의 주제 :
  • 문자를 double 형으로 변환하기
  • 키보드로 부터 float 숫자를 입력(:12) 받기
  • Math 클래스
  • 루트
  • Trig 함수
  • Pl
  • Not a Number (NAN)
또한 Java의 Math 클래스를 이용해서 삼각함수, log, 루트 등의 수학계산을 하는 프로그램을 만들어 볼 것이다. Math 클래스에서 제공하는 이러한 메서드들은 보통 float 숫자들을 다룬다.

문제 doublefloat를 표현하기 위해서 몇바이트의 공간이 사용되는가.

Floats 의 크기

float 데이터는 32비트의 크기가 필요하며, double는 64비트의 저장공간이 필요하다. 만약 키보드로 부터 이러한 숫자들을 입력받는다면, 반드시 float나 double로 변환시켜주는 과정을 거쳐야 한다. 반대로 계산된 결과를 모니터나 text file(:12)로 저장하기 위해서, float 데이터를 문자로 변환시켜줘야 할 것이다.

Type 크기 범위
float 32bit -3.4E+38 ~ 3.4E+38
double 64bit -1.7E+308 ~ 1.7E+308
float와 double은 표현가능한 범위와 정확도에 있어서 차이가 있을 뿐 서로 동일하다.

문제 intfloat는 모두 32bit의 크기를 가진다. int값 211과 float값 221.0은 같은 비트의 패턴을 가지는가 ?

키보드로 부터 double 값 읽어들이기

비록 int와 float가 같은 32bit의 크기를 가지지만, float 같은 경우 소숫점을 표현하기 위한 정보가 들어가기 때문에 int와는 전혀다른 비트패턴을 가지게 된다. 그러므로 221과 221.0은 전혀 다른 패턴을 보여주게 된다.

float 입력은 java.io를 이용해서 integer 형을 다룰 때와 비슷하게 처리할 수 있다. Scanner객체를 이용하면 입력스트림을 float 혹은 double로 변환할 수 있다. 각각의 변환작업을 위해서 nextFloat()와 nextDouble()를 사용하면 된다.

여기에 키보드로 문자 읽어들인 값은 double로 변환한후 2배만큼 곱해준 결과를 출력해주는 프로그램이 있다.
// This program requires Java 1.5 or higher
//
import java.io.*;
import java.util.Scanner;

class DoubleDouble
{
  public static void main (String[] args)
  {
    double value;
    Scanner scan = new Scanner( System.in );
 
    System.out.print("Enter a double:");
    value = scan.nextDouble();

    System.out.println("value: " + value +" twice value: " + 2.0*value );
  }
}

다음은 실행결과다.
$ java DoubleDouble
Enter a double:17.2
value: 17.2 twice value: 34.4

문제 만약 211과 같은 int형 값을 입력하면 어떻게 될까?

예외

Scanner의 nextDouble 메서드는 double로 해석가능한 문자까지를 읽어서 double 형으로 변환한다. 만약 입력의 시작이 double로 변환불가능한 값이 들어온다면, 자바는 exception(예외)를 발생시키고, 프로그램을 종료시킬 것이다. (뒤에 예외를 다루는 방법에 대해서 알아볼 것이다)
$ java DoubleDouble
Enter a double:data
Exception in thread "main" java.util.InputMismatchException
        at java.util.Scanner.throwFor(Scanner.java:819)
        at java.util.Scanner.next(Scanner.java:1431)
        at java.util.Scanner.nextDouble(Scanner.java:2335)
        at DoubleDouble.main(DoubleDouble.java:13)

처음 입력된 값이 double로 변환가능한 값이라면, 최대한 변환가능한 곳 까지를 읽어들여서 double로 변환하게 된다.
$ java DoubleDouble
Enter a double:3.14 and more number
value: 3.14 twice value: 6.28

문제 다음의 실행결과 값을 예측해 보자.
$ java DoubleDouble
Enter a double: -97.65
????

과학 표기법

과학표기법은 double를 다른 방식으로 표현할 수 있다. E를 사용하게 되는데, 이것은 10에 N승을 의미한다. 예를들어 1.314E+1 은 1.314 * (10^1)으로 13.14와 같다.
$ java DoubleDouble
Enter a double:1.234E+9
value: 1.234E9 twice value: 2.468E9

E를 소문자로 사용해도 관계 없다.
$ java DoubleDouble
Enter a double:-7.001e-2
value: -0.07001 twice value: -0.14002

문제 7.0E-2 의 의미는 ?

두개의 Double 값을 더하기

여기에 두개의 Dobule 값을 받아들여서 더하는 프로그램이 있다.
import java.io.*; import java.util.Scanner; class AddDoubles { public static void main (String[] args) { double first, second, sum; Scanner scan = new Scanner( System.in ); // Read in the first double System.out.print("Enter the first double:"); first = scan.nextDouble(); // Read in the second double // Compute the sum and write it out } }
빈칸을 채워서 완전한 프로그램을 만들어 보도록 하자.

완성된 프로그램

다음은 빈칸을 채운 완전한 프로그램이다.
import java.io.*;
import java.util.Scanner;

class AddDoubles
{
  public static void main (String[] args)
  {
    double first, second, sum;
    Scanner scan = new Scanner( System.in );
 
    // Read in the first double
    System.out.print("Enter the first double: ");
    first = scan.nextDouble();

    // Read in the second double
    System.out.print("Enter the second double: ");
    second = scan.nextDouble();

    // Compute the sum and write it out
    sum = first + second;
    System.out.print("Sum: " + sum );
  }
}

다음은 프로그램을 실행시킨 결과다.
$ java AddDoubles
Enter the first double: 10.24
Enter the second double: 8.88
Sum: 19.12

문제 sum변수를 사용하지 않고, 동일한 결과를 출력하는 프로그램으로 만들어 보자.

story problem

당신은 식당을 운영하고 있고, 제공한 식사에 대한 세금을 계산하는 프로그램을 만들기를 원하고 있다. 이 프로그램은 식사의 값을 받아들인다면 6%의 세금을 뗀다. 여기에 20%의 팁을 계산해서 수익을 계산한다. 계산을 하기 위해서 double를 사용하도록 하자.
import java.io.*;
import java.util.Scanner;

class RestaurantBill
{
  public static void main (String[] args)  

  {
    
    double basicCost;

    
    

    System.out.println("basic cost: " +  basicCost + " total cost: " + 
         );
  }
}

위 프로그램이 완성되기 위해서는 아래와 같은 수치연산을 위한 코드가 필요할 것이다. (basicCost + basicCost*0.06 + basicCost*0.20) System.out.print("Enter the basic cost: "); Scanner scan = new Scanner( System.in ); basicCost = scan.nextDouble();

이제 아이디어를 얻었으니, 이걸 이용해서 빈칸을 채운 완전한 프로그램을 만들어 보도록 하자.

완성된 세금계산 프로그램

다음은 완성된 세금계산 프로그램이다. 컴파일 한 다음에 테스트해 보도록 하자.
import java.io.*;
import java.util.Scanner;

class RestaurantBill
{
  public static void main (String[] args)  

  {
    Scanner scan = new Scanner( System.in );
    double basicCost;

    System.out.print("Enter the basic cost: ");
    basicCost = scan.nextDouble();

    System.out.println("basic cost: " +  basicCost + " total cost: " + 
         (basicCost + basicCost*0.06 + basicCost*0.20) );
  }
}

향상된 세금계산 프로그램

위 프로그램은 주어진 업무를 무리없이 수행하긴 하지만, 유연하지 않다는 문제점을 가진다. 세율이나 팁금액이 변경될 수 있는데, 그 때마다 프로그램을 새로 만들어야 하기 때문이다. 특히 팁같은 경우는 자주 바뀔 수 있을 것이다. 이제 팁까지 따로 받아들여서 수입을 계산가능 하도록 프로그램을 변경해 보도록 하자. 이경우 basic cost 와 tip 두개의 입력을 받아들여야 할 것이다.
import java.io.*;
import java.util.Scanner;

class RestaurantBill
{
  public static void main (String[] args)  
  {
    Scanner scan = new Scanner( System.in );
    double basicCost;
    double tipPercent;

    System.out.print("Enter the basic cost: ");
    basicCost = scan.nextDouble();
    System.out.print("Enter the tip percentage: ");
    tipPercent = scan.nextDouble();

    System.out.println("basic cost: " +  basicCost + " total cost: " + 
         (basicCost + basicCost*0.06 + basicCost*tipPercent) );
  }
}

자바로 만드는 계산기

거의 대부분의 계산기가 sine, log, square root를 위한 계산 기능을 포함하고 있다. java의 경우 Math 클래스를 이용하면, 이러한 계산이 가능하다. 일반적으로 이러한 프로그램들은 double를 받아들이고, 결과값으로 double를 리턴한다.

다음은 root계산을 하는 간단한 프로그램이다.
import java.util.Scanner;

class SquareRoot
{
  public static void main (String[] args)
  {
    Scanner scan = new Scanner( System.in );
    double value;

    // read in a double
    System.out.print  ("Enter a double:");
    value = scan.nextDouble();
    
    // calculate its square root
    double result = Math.sqrt( value );
    
    // write out the result
    System.out.println("square root: " + result );
  }
}
Math.sqrt()가 루트계산을 위한 메서드이다. Math 클래스에 포함된 메서드임을 알 수 있다. 이것은 static 메서드이다. static 로 선언되면, 클래스이름을 가지고 바로 메서드를 호출해서 사용할 수 있다. 일반적인 클래스가 클래스 이름으로 객체를 만든다음 메서드를 호출하는 것과는 다름을 알 수 있다.

문제 Math 클래스는 log메서드도 제공하고 있다. 위의 프로그램을 log 계산이 되도록 변경해 보자.

sqrt 메서드

위 프로그램을 실행시켜보자.
$ java SquareRoot
Enter a double:8
square root: 2.8284271247461903

실행해서 결과를 보는데까지 아무런 어려움도 없을 것이다. 키보드 입력을 알아서 float point로 변환시켜주고, double의 결과값은 다시 문자로 바꿔서 모니터 화면에 출력해주기 때문이다.

http://java.sun.com 을 방문하면, 다양한 클래스와 메서드들의 사용방법이 담긴 문서를 다운로드 받을 수 있다. 혹은 gool(:12)과 같은 검색엔진에서 sqrt java와 같은 검색어를 이용해서 메서드의 사용법을 알아낼 수 있다.

문제
  • 문서를 참고해서 sqrt메서드가 어떤 타입의 인자를 가지는지 알아보도록 한다.
  • sqrt 메서드는 어떤 타입의 결과를 리턴하는가.
  • sqrt는 static 메서드인가?

NaN

java.com의 문서를 참고하면 sqrt 메서드에 대한 자세한 내용을 알수 있을 것이다. sqrt로 계산가능한 올바른 인자를 넣었다면, double 타입의 결과값을 리턴할 것이다. 만약 잘못된 인자를 넣었다면, NaN을 리턴하게 된다.

NaNNot a Nnumber 의 줄임말로, 계산이 블가능한 인자를 넣었을 때 리턴된다.
$ java SquareRoot
Enter a double:-8
square root: NaN

이 메서드는 64-bit 패턴의 NaN을 리턴한다. println 메서드가 화면에 "NaN"을 출력하지만, 실제 bit 패턴이 문자열인건 아니다.

자바가 산업현장에서 사용될 경우에는 각 함수로 넘어가는 인자값의 범위에 대한 체크를 분명히 해야 한다. java.com의 문서에는 체크해야 하는 가장작은값 가장 큰값의 범위등이 명시되어 있다.

문제 아래 코드의 출력값은 ? int x = 9; System.out.println( Math.sqrt( x ) );

자동변환

sqrt는 인자값으로 double를 받는다. 만약 int 형이 주어진다면, 알아서 double 로 변환시킨다음 계산을 하게 된다. 많은 메서드들이 알아서 데이터 타입을 변환해주긴 하지만, 모든 메서드들이 데이터 타입을 변환해주는 건 아니다. 또한 사용하는 메서드가 데이터 타입을 알아서 변경해주는 것을 알고 있다고 하더라도, 코드를 명확히 하기 위해서 프로그래머가 직접 데이터타입을 변경시켜줄 수가 있다. 이렇게 타입을 변경시키는 것을 type cast (형변환) 이라고 한다. int x = 9; System.out.println(Math.sqrt( (double)x ))

비록 sqrt 메서드가 자동으로 데이터 타입을 변경시켜주기는 하지만, 가능하면 형변환을 통해서 코드상에 분명하게 명시해 두는게 좋다. 어떤 메서드들은 형변환이 되어야만 하는 경우도 있다.

문제 아래 코드의 결과를 예측하라. int x = 1; int y = 9; System.out.println( Math.sqrt( x/y ) );

Hidden Integer Division

위의 결과는 0.0 이 나온다. 우리가 예상했던 결과는 0.333333 이다. 이유는 내부의 나눗셈 연산에 있다. x와 y의 데이터 타입이 int 형이므로, 내부의 결과는 0 이되고, Math.sqrt(0) 이 계산되기 때문이다.

이 경우 x/y 에 대해서 double로 형변환을 해야 한다.

문제 다음 코드는 원하는 결과를 출력하는가 ?
int x = 1;
int y = 9;
System.out.println( Math.sqrt( (double)x/y ) );

위의 방식대로 x를 형변환하면, 나눗셈연산시 y도 형변환이 된다. 이제 나누기 결과는 0.11111... 이 되고, sqrt 결과로 0.33333...이 리턴된다.

PI

일반 공학용 계산기를 다룰 때 보다는 이것저것 신경써줘야 할게 있지만, Java로도 충분히 복잡한 계산을 할 수있다.

이제 PI값을 알아보자. 이것은 다양한 수학적인 계산에 사용되며, 무한대의 자릿수를 가지고 있는 것으로 유명하다. Java 문서는 PI를 다음과 같이 설명하고 있다.
PI

public static final double PI

The double value that is closer than any other to pi, 
the ratio of the circumference of a circle to its diameter.

PIfinal로 선언되어 있는데, 이는 값이 변할 수 없다는 것을 의미한다. C(:12)와 같은 언어에서의 상수(:12)개념이다. 즉 PI 값은 별도의 수학적인 연산을 통해서 구해오는게 아니고, 미리계산된 값을 불러온다.

만약 PI 값을 사용할 일이 있다면, 다음과 같이 하면된다.
double x = Math.PI;
아래의 프로그램을 컴파일 후 실행시켜보자. PI 값을 알 수 있을 것이다.
import java.util.Scanner;

class Pi
{
  public static void main(String[] args)
  {
    double x = Math.PI;
    System.out.println("PI "+x);
  }
}

문제 Java 는 Math.cos() 와 같은 삼각함수를 제공한다. Math.cos()는 어떤 데이터를 받아들일까 ?

Radians

모든 계산기와 거의 대부분의 프로그래밍 언어는 radians을 삼각함수의 인자로 받아들인다. Java 프로그래밍 언어역시 마찬가지다.

import java.io.*;
import java.util.Scanner;

class CosineCalc
{
  public static void main (String[] args)  
  {
    double value;
    Scanner scan = new Scanner( System.in );
 
    System.out.print("Enter radians:");
    value = scan.nextDouble();
    
    // calculate its cosine
    double result = Math.cos( value );
    
    // write out the result
    System.out.println("cosine: " + result );
  }
}
다음은 실행 결과다.
$ java CosineCalc
Enter radians:1.5
cosine: 0.0707372016677029

문제 Matn.cos() 메서드의 인자와 리턴값의 데이터 타입은 ?