2018년 2월 28일 수요일

소수점 제거 함수 삼총사 ceil(), floor(), round()

이번 글은 굉장히 유명한 수학 함수 3가지를 적어보는 글입니다. 그리고 오랜만에 구어체가 아닌 존대말(?)로 쓰는 글이기도 하겠네요.

이번에 언급하는 함수 세 가지, 즉 ceil(), floor(), round() 함수는 C 언어 시절부터 쓰이던 수학 함수 중 일부입니다. 이 함수 들은 현대의 거의 대부분의 언어에서도 대부분 동일한 이름으로 제공될 정도로 필수 함수이지요.

이 함수들의 용도 및 활용법을 Swift 기준으로 정리해 보겠습니다. 물론 다른 언어에서도 거의 동일하니 언어가 의미가 있는지는 잘 모르겠지만요.

ceil()

ceil() 함수는 소수점 아래의 숫자가 있으면 소수점 아래를 다 버리고 정수부에 1을 더해주는 함수입니다.
ceil(9.2)   // 10
ceil(10.1)  // 11
ceil(10.5)  // 11
ceil(10.9)  // 11
ceil(11)    // 11
ceil 은 '천장을 만들다' 라는 의미가 있는데 쉽게 이해가 가능할 거라고 봅니다. 소수점 아래가 있으면 무조건 올려 버리는 것이지요. 개인적인 표현으로 'X나 민감한 반올림' 라고 부릅니다.

floor()

floor() 함수는 대충 ceil() 함수와 반대되는 역활을 한다고 볼 수 있습니다. 즉 소수점 아래를 그냥 버려버리는 것이지요.
floor(9.2)  // 9
floor(10.1) // 10
floor(10.5) // 10
floor(10.9) // 10
floor(11)   // 11
floor 는 바닥이라는 의미이지요. 의미 상으로도 ceil 과 반대됩니다.

round()

반올림이라는 말을 들어보셨을 겁니다. round() 함수는 소수점 아래를 버리되 반올림을 해주는 함수입니다. 0.5 이상은 1로 올리고 그 외는 0 으로 처리한다는 말이지요.
round(9.2)  // 9
round(10.1) // 10
round(10.5) // 11
round(10.9) // 11
round(11)   // 11
반올림은 익숙하시리라 생각됩니다.

활용 예제

'소수점 몇 자리 아래를 버리고 싶다' 라는 문제가 있다면 어떻게 해결 할 수 있을까요? 소수점 아래를 버려버리는 floor() 함수를 활용하는 방법이 있습니다. 아래 코드는 소수점 아래 3째 자리 까지만 남겨두고 그 아래는 버리는 코드입니다.
let input = 10.123456789
let output = floor(input * 1000) / 1000
// output == 10.123
input 이 입력 값이고 output 이 출력 값입니다. floor() 함수를 이용해 소수점 아래를 버리는데 그 전에 자리수 만큼 곱셈을 해서 소수점 위치를 조절하고 다시 나누셈으로 소수점 위치를 돌리는 노하우입니다. 위에서 쓰인 1000 이라는 값에서 0이 3개 라는 것을 이해하실 수 있겠지요? 두 자리만 처리한다면 100으로 바꾸면 되겠네요.

이런 방식과 비슷하게, 소수점 몇몇 자리 아래는 버리되 반올림을 하고 싶다면 floor() 대신 round() 함수를 사용하면 될 수 있겠지요. 아래 예제는 소수점 3째 자리 아래를 버리되 반올림을 하는 예제입니다.
let normalize: ((Float) -> Float) = { (input) in
    return round(input * 1000) / 1000
}

normalize(95.121100)  // 95.121
normalize(97.996778)  // 97.997
두 가지 예를 보기 위해 일부러 normalize() 라는 클로져 형식을 만들었다는 점과 floor() 대신 round() 함수가 쓰였다는 점을 제외하고는 별로 다를 건 없습니다.

이 외에도 이 함수들은 iOS / macOS 에서 픽셀 단위를 계산하기 위해 쓰일 때가 있습니다. CGPoint 가 표현하는 좌표는 CGFloat 라는 부동소수점 타입인데, 특히 레티나 처럼 물리 해상도와 논리 해상도가 다를 경우 0.5 픽셀은 화면 상에 흐릿한 점을 찍게 만들 수도 있기 때문에 이를 위의 세 가지 함수로 소수점 아래를 날려버려서 1 픽셀 단위로 계산을 해야 하는 경우가 있을 수도 있습니다. 물론 3D 연산 처럼 정확하게 계산해야 한다면 이야기가 다르지만요.

왜 부동소수점 타입을 리턴하는가

위에서 살펴본 함수들은 모두 정수(integer) 즉 소수점 아래가 없는 숫자를 리턴하는 함수입니다. 그런데 전부 부동소수점(floating number) 이라는 '실수를 표현하기 위한 타입'을 리턴합니다. 왜 그런 것일까요?

이는 부동소수점이 숫자 단위를 표현하는 방법 때문입니다. 부동(floating) 이라는 말은 소수점의 위치가 고정되지 않았다는 말입니다. 1.0 혹은 100000000000.0 혹은 0.000001 등 소수점의 위치는 제각각입니다. 그렇기에 소수점의 위치를 표시하기 위한 특수한 필드를 가지고 있는 타입이라고 생각하며 될 것 같습니다. 물론 소수점 뿐만 아니라 숫자의 단위를 표현하는 방법도 E가 붙어서 표기되는 지수 형태 처럼 정의되기 때문에 일반적인 정수와는 데이터 표현 방법이 다릅니다.

만약 이 함수들이 정수를 리턴하는 형태였다면? 입력 값에 따라서 정수로는 표현하지 못 하는 큰 값이 리턴 되어야 할 수도 있게 됩니다. 이렇게 되면 무엇을 리턴할 수 있을까요? 오버플로우 된 값을 리턴해야 할까요? 뭐가 되던 결국 부정확한 결과를 얻게 된다는 말입니다.

그래서 이런 소수점을 다루는 함수들은 모두 부동소수점을 반환하게 됩니다.

[관련글] 스위프트(Swift) 가이드

댓글 3개 :

익명 :

설명을 좀 헷갈릴 수 있게 쓰셔서 글 남깁니다. ceil의 경우 소수점 아래를 버리고 1을 더한다고 하셨는데 소수점 아래를 버린다는 건 floor로 착각할 여지가 있습니다. ceil을 'x나 민감한 반올림'이라 부른다고 하셨는데 이건 반올림이 아니라 그냥 올림입니다. '반'이 붙지 않습니다. 개인적으로는 민감한 부분이 어디인지 궁금하네요. 오직 round만이 반올림이라 부르는 거고, floor의 경우 아예 내림이라 부릅니다.

Seorenn :

ceil은 '올림' 이고, floor는 '내림' 이다 라는 한글 표현은 굉장히 좋은 것 같습니다. 좋은 가르침 감사합니다.

이 글은 '내림'과 '올림' 이라는 표현은 모르고 '반올림(round)'만 알고 있는 상태에서 적은 글이라고 이해하시면 될 것 같습니다.

익명 :

비속어를 굳이........ 예시가 안맞는듯