본문 바로가기
강의/컴퓨터과학과 프로그래밍 입문

5. 실수형 (Floating point numbers)

by 소꿍 2020. 3. 25.

강의링크: https://youtu.be/Pfo7r6bjSqI?si=Xb_yQlLEwwPqsVdj

 

- python의 두 종류 수

int(integer)

Arbitrary precision integers(원하는만큼 큰 수를 만들 수 있음) - 파이썬에만 있는 것

 

ex) >>> a = 2 ** 1000 를 입력하고 a를 출력하면 긴 숫자 끝에 L이 붙어서 나옴(Long)

이는 내부에 긴 형식의 특정한 정수임을 의미함(particular integer in what it calls it's internal long format)

>>> b = 2 ** 999 를 입력하고 a/b를 하면 2가 아닌 2L를 돌려줌(한 번 L을 얻으면 L의 형태를 유지하기 때문)

 

Float(floating point): 프로그래밍 언어에서 실수를 나타내는 방법

ex) >>> x = 0.1
>>> x
0.10000...1 -> 0.1이 아니라 이렇게 나타나는 것은 컴퓨터 내부에 숫자가 나타나는 방법과 관련되어 있음

다른 프로그래밍 언어처럼 python은 IEEE floating point standard를 사용해 숫자를 나타낸다.(IEEE 754) -> 가수(mantissa)와 지수(exponent)의 쌍으로 숫자를 나타냄(부동 소수점 수)
*scientific notation(큰 수를 나타내는 방법)

컴퓨터는 이진 시스템에서 작동하므로 2를 제곱하여 수를 나타낸다
1 <= mantissa < 2
exponent의 범위는 -1022 : 1023
십진수로 이 범위를 나타내면 +- 10 ** 308 -> 이는 컴퓨터가 내부에 가지고 있는 words와 관련된다.

그 words가 64 Bits(전에는 8, 16, 32 Bits였음)
1 Bit sign
11 exponent
52 mantissa

17 decimal digits
17자릿수의 숫자들을 십진법으로 나타낼 수 있다. (?)

부동 소수점 수를 다룰 때 17자리 수보다 더 큰 수는 나타낼 수 없음(프로그래밍 언어에서)

ex) 1/8 = 0.125
Base 10 : 1.25(mantissa) * 10*-1
Base 2 : 1.0 * 2*-3(0.001)

ex2) 1/10
Base 10 : 1 * 10*1
Base 2 : .00011001100... -> 1/10의 근사치만 얻을 수 있음
이는 십진수 분수인 1/10을 정확히 표현하는 유한한 이진수가 없기 때문
컴퓨터에서 이진 근사치의 십진수 값을 출력하려고 했기 떄문에 python은 십진법으로 이를 출력해 준다.

repr() : python에서 무엇을 나타낼 때 내장함수 repr을 사용하는데, 숫자를 문자로 바꿔 출력한다. ?

float형은 반올림하여(round) 17자리의 수로 나타내진다.
ex) x = 0.0
for i in range(10): s += 0.1
>>> s
0.99999...89
>>> print(s)
1.0 -> 이는 프린트 명령이 반올림을 해서 1.0을 출력해 보여준 것이다.
하나의 내부가 아니기 때문에 1이 아닌 다른 값을 얻는다.

in Latin, caveat computor (?)

- Worry about '==' on floats
>>> import math
>>> a = math.sqrt(2)
>>> a*a == 2
False -> 위에서 'a = math.sqrt(2)'을 입력했을 때 저장된 값이 근사치이기 때문에 False이다.
>>> a*a
2.0000...4

따라서 소수점을 비교할 때 '=='을 사용하는 것은 위험하다.
equlity를 위한 test는 할 수 없고, 근사치를 위한 test만 가능하다.

abs(a*a - 2.0) < epsilon
근삿값에서 실제 값(2.0)을 뺀 값이 machine epsilon보다 작으면, 둘을 같은 값으로 볼 수 있다.

might not be an exact answer
정수와 달리 실수에서는 모든 추측을 계산할 수 없다. 실수를 셀 수 없기 때문.

그래서 guess, check, & improve 방법을 사용한다.

 


- successive approximation(연차 근사법)
guess = initial guess
    for iter in range(100):
        if f(guess) close enough: return guess
        else: guess = better guess
    ERROR

ex) squareRootBi

def squareRootBi(x, epsilon):
	"""Return y s.t. y*y is within epsilon of x"""
	assert epsilon > 0, 'epsilon must be postive, not' + str(epsilon)
	low = 0
	high = max(x, 1)
	guess = (low + high)/2.0
	ctr = 1
	while abs(guess**2 - x) > epsilon and ctr <= 100:
		#print('low:', low, 'high:', high, 'guess:', guess)
		if guess**2 < x:
			low = guess
		else:
			high = guess
		guess = (low + high)/2.0
		ctr += 1
	assert ctr <= 100, 'Iteration count exceeded'
	print('Bi method. Num. iterations:', ctr, 'Estimate:', guess)
	return guess


여기서 Bi는 Bi-section method(이분법)
이분법에서는 가능한 답들이 선형으로 배열된 것을 가짐(line)
Bi-section인 이유는 답을 선택할 때 항상 라인에 따라 절반을 선택하게 되기 때문
line에서 범위와 guess를 지정 -> guess가 답이 아닐 경우 절반을 버리고 나머지 절반에서 다시 guess
이 과정은 대수(로그)적으로 진행하기 때문에 오래 걸린다.

Assumes x >= 0 and epsilon > 0
이 가정을 검사하기 위해
assert x >= 0
x가 0보다 같거나 크다는 가정을 test해서 이 가정이 맞으면 아무것도 하지 않고 다음 문장으로 넘어가고, 가정이 맞지 않으면 메시지(아래)를 출력한 후 프로그램을 멈춘다.
'x must be non-negative, not' + str(x)
그 다음 단계에서 선형의 범위를 설정한다. 이때 범위 값은 정확한 것에 초점을 두기보다는 확실하게 가능한 범위를 설정한다.
low = 0, high = x(x보다 작고 0보다는 같거나 크다는 범위를 지정)
그리고 이 범위의 중간에 있는 어떤 값을 고른다.
(low + high) / 2.0
카운트(ctr)를 1로 두고
프로그램이 작동하지 않으면 출력문을 통해 왜 그런지 알 수 있다. (?)
guess ** 2 < x : 이 경우에는 추측하기 위한 low값을 guess로 바꾸고
else: 그렇지 않으면 high 값을 guess로 바꾼다.
그리고 다시 그 범위 중간에 있는 값을 고르고 카운트 값을 증가시킨다.
(low + high) / 2.0, ctr += 1

댓글