2019.03.10 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 엡실론(epsilon)

먼저 엡실론을 파이썬에서
sys.float_info.epsilon이라고 서칭해보면
2.220446049250313e-16와 같은 값이 나온다. 굉장히 작은 숫자이다.

엡실론의 정의는 epsilon : difference between 1 and the next representable float

==> 1과 그 다음에 나올 수 있는 값의 차이를 이야기 한다.

sys.float_info.dig ----> 15 (digit 는 자리수)

그럼 여기서 말하는 15자리는 무엇인가?

먼저, 정밀도에 대해 소수점 아래 6자리 실수를 정밀도라고 적힌 책은 버려야 한다.(by teacher)

2진수 몇자리수로 10진수 1자리를 표현할 수 있을까?
10진수 1의 자리는 9까지 표현할 수 있으면 되는데 9는 ----> 1001(2) 이므로 4자리가 있으면 다 표현할 수 있다.
2진수 1111 채우면 10진수로 15까지 표현 가능 ----> 9보다 15가 크므로 1자리수는 완전히 커버 가능함
1111 111 7자리를 채우면 10진수 127까지 표현 가능 ----> 2자리수는 완벽히 커버가능
1111 1111 11 10자리를 채우면 10진수 1023까지 표현 가능 ----> 3자리 완벽히 커버가능

floating에서 가수부는 총 23bit에 기본 1.0 1bit를 더해 총 24bit 까지 커버가능한데.
과연 1111 1111 1111 1111 ----- 24까지 했을 때 어디까지 커버가 가능할까?

10진수로 나타내면 16,777,215 이다. 즉 완벽히 커버 가능한 자리는 9.999.999 총 7자리에 대해 커버가 가능한다. 이는 6 ~ 7 자리를 이야기 할 수 있고 10진수로 7자리를 표현할 수 있다는 정밀도가 있는 것이다.

double에서는 mantisadigit - 53이다.
1111 1111 1111 1111----> 53개까지 했을 때 어디까지 커버가 가능할까?
9,007,199,254,740,991 ---> 총 15자리까지 완벽 커버가능하다.

여기서 제일 마지막 1은 더 이상 커져도 신뢰할 수 없다.

엡실론

실수 1.0 다음 수에 대해 생각해 보자.

floating에서
1.0 x 2 ** 0 그 다음은 1.00000000000000000000001(23자리 뒤의 1) x 2 ** 0 이고
따라서 차이는 1.0 x 2 ** -23로 나타낼 수 있다.

double은 1.0x 2 ** -52
sys.float_info.epsilon == 2.0**-52 해보면 True가 나오게 된다.

double로 변하면서 정밀도가 훨씬 좋아져 epsilon의 값도 많이 작아지게 된다.

epsilon 컴페어리즘

그럼 이 엡실론을 어디에 쓰일 수 있을까??
float a = 10.5 다음에 쓰일 수 있는 수 사이의 차이를 구해보자.

10.5 = 8 + 2 + 0.5 = 2 ** 3 + 2 ** 1 + 2 * -1 = 1.0101(2) x 2 ** 3
diff = 2 ** e x epsilon( 2 ** -23)
2 ** 3 x 2 ** -23 = 2 ** -20의 간격을 두고 다음 수가 쓰일 수 있다.

하지만 이렇게 하면 너무 시간이 오래 걸린다.

엡실론 계산

매번 2진수로 바꾸고 지수부를 구하기에는 시간이 너무 오래 걸리므로 지수부보다 num은 무조건
크므로 지수부 대신 num을 쓰게 되면 사이 간격을 나타내는데 더 보수적으로 쓰게 되는 것이다.

하지만 여기서는 더 보수적으로 잡아서 표현범위를 나타내는 지수부를 그대로 num으로 근사시켜 사용한다.
이렇게 되게 되면 우리는 너무나도 쉽게 다음에 쓰일 수와의 차이를 알 수 있다.

실수 비교

따라서 실수는 if(a==b)처럼 직접 비교하면 안된다.

그러므로 a,b를 비교하는 함수를 작성해 비교한다. (오히려 배우고 나니 문제가 더 커진 기분이다.)

def is.equal(a,b):
    return True of False

여기서 실수 비교 기법 2가지가 나온다.

  • 절대 비교 기법
def is_equal(a,b):
    return |a-b| <= 1e-10    

두 수의 차이가 1e - 10 보다 작다면 같다고 생각해도 무방하다.
하지만 1e - 10을 어떻게 정할 것인가? 함수를 만들어 놓은 이유는 계속해서 쓰기 위해서 인데
이렇게 해놓으면 매번 함수의 구현을 바꾸어야 하므로 매번 내부함수를 바꿔야 하므로 추상화에 위반된다.

from math import fabs
def is_equal(a,b):
    return fabs(a-b)<= 1.0e-10    #fabs는 절대값을 나타내는 math모듈에 있는 함수??이다.

if is_equal(a,b):
    print("이 정도 차이면 봐줄게")
else:
    print("이건 같은 수가 아니징")

따라서 우리는 매번 1e - 10을 필요에 따라 바꾸어야 하는 문제가 발생한다.
1e - 10을 수학적으로 족보를 가진 애로 만들어야겠다.

  • 상대 비교 기법

relative Error = fabs(a-b)/max(fabs(a). fabs(b)) max는 둘중에 큰수
분수로 표시하는 이유는 5.5와 5.3의 차이 그리고 55와 53의 차이를 똑같게 만들어 준다.

from math import fabs
import sys
def is_equal(a, b, w=0):

    """
    is_equal(a, b, w = 0) ---> bool

    w는 가중치입니다. w를 조정함으로서 diff의 범위를 조정할 수 있다.

    w를 0부터 늘려가며 상대 오차 범위를 조정해주세요    

    """
    ep = sys.float_info.epsilon
    diff=fabs(a-b)    
    return diff <= max(fabs(a), fabs(b))*ep*(2**w) #큰 값을 하는 이유는 큰수를 해서 조금 더 오차를 인정해주자 그런 느낌으로 보면 된다.

위와 같은 것을 epsilon 컴페어리즘이라고 한다.

즉 오차범위를 어디까지 허용할지 내가 직접 함수를 만들어서 가중치를 조절해가며 쓸 수 있는 것이다.

원래
a = 3.0
b = 1.0 * 3
a == b 하면 false가 나오나

위의 값에 비교해보면

if is_equal(a,b):

        print('things')

things

가 나오게 된다.

+ Recent posts