부동소수점 리터럴

부동소수점 리터럴

실수값을 그대로 코드에 적어야 할 때가 있어요. 그런 값들을 부동소수점 리터럴(floating-point literal) 이라고 부르는데요, Go에서는 이걸 십진수로도, 16진수로도 쓸 수 있어요. 두 형태가 어떻게 생겼는지, 그리고 어떤 조합은 허용되고 어떤 조합은 안 되는지 하나씩 짚어볼게요.

출처: Go Specification

본문

부동소수점 리터럴은 하나의 부동소수점 상수를 십진수 또는 16진수로 나타낸 표현이에요.

십진 부동소수점 리터럴은 정수부(십진 숫자), 소수점, 소수부(십진 숫자), 지수부(e 또는 E 뒤에 선택적 부호와 십진 숫자)로 이루어져요. 정수부와 소수부 중 하나는 생략할 수 있고, 소수점과 지수부 중 하나도 생략할 수 있어요. 지수값 exp는 가수(정수부와 소수부)를 10exp배로 스케일해요.

16진 부동소수점 리터럴은 0x 또는 0X 접두어, 정수부(16진 숫자), 기수점, 소수부(16진 숫자), 지수부(p 또는 P 뒤에 선택적 부호와 십진 숫자)로 이루어져요. 정수부와 소수부 중 하나는 생략할 수 있고, 기수점도 함께 생략할 수 있지만 지수부는 반드시 필요해요. (이 문법은 IEEE 754-2008 §5.12.3에서 주어진 것과 일치해요.) 지수값 exp는 가수를 2exp배로 스케일해요 Go 1.13.

가독성을 위해 밑줄 문자 _를 base 접두어 뒤나 연속된 숫자 사이에 넣을 수 있는데요, 그런 밑줄은 리터럴 값에는 영향을 주지 않아요.

float_lit         = decimal_float_lit | hex_float_lit .

decimal_float_lit = decimal_digits "." [ decimal_digits ] [ decimal_exponent ] |
                    decimal_digits decimal_exponent |
                    "." decimal_digits [ decimal_exponent ] .
decimal_exponent  = ( "e" | "E" ) [ "+" | "-" ] decimal_digits .

hex_float_lit     = "0" ( "x" | "X" ) hex_mantissa hex_exponent .
hex_mantissa      = [ "_" ] hex_digits "." [ hex_digits ] |
                    [ "_" ] hex_digits |
                    "." hex_digits .
hex_exponent      = ( "p" | "P" ) [ "+" | "-" ] decimal_digits .

위 문법이 실제로 어떻게 쓰이는지는 예시로 확인하는 게 빠르죠. 아래는 유효한 예와 그 값을 계산해 본 결과예요.

0.
72.40
072.40       // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5
1_5.         // == 15.0
0.15e+0_2    // == 15.0

0x1p-2       // == 0.25
0x2.p10      // == 2048.0
0x1.Fp+0     // == 1.9375
0X.8p-0      // == 0.5
0X_1FFFP-16  // == 0.1249847412109375
0x15e-2      // == 0x15e - 2 (integer subtraction)

0x.p1        // invalid: mantissa has no digits
1p-2         // invalid: p exponent requires hexadecimal mantissa
0x1.5e-2     // invalid: hexadecimal mantissa requires p exponent
1_.5         // invalid: _ must separate successive digits
1._5         // invalid: _ must separate successive digits
1.5_e1       // invalid: _ must separate successive digits
1.5e_1       // invalid: _ must separate successive digits
1.5e1_       // invalid: _ must separate successive digits

좋은 예시도 보셨으니, 이제 헷갈리기 쉬운 지점 몇 개만 정리할게요.

  • 십진 리터럴은 지수에 e/E를 쓰는데, 16진 리터럴은 반드시 p/P를 써요. 그래서 1p-2처럼 p 지수인데 가수가 16진이 아니면 오류가 되고, 반대로 0x1.5e-2처럼 16진 가수에 e 지수를 붙이면 오류가 돼요.
  • 16진 리터럴은 지수부가 필수예요. 가수에 숫자가 없는 0x.p1은 물론, 지수까지 없으면 아예 성립이 안 되죠.
  • 밑줄은 연속된 숫자를 구분하는 역할만 해요. 그래서 1_.5, 1._5, 1.5e_1처럼 숫자 사이가 아닌 자리에 _가 있으면 전부 오류예요.
  • 0x15e-2를 주의하세요. 이건 0x로 시작하지만 e-2가 지수부가 아니라 정수 뺄셈으로 해석돼요. 그래서 값이 0x15e - 2예요. 16진 지수부는 p/P로만 붙기 때문이에요.

더 알아보기