숫자 리터럴 표기 — _ · 지수 · 진법
시트의 숫자 셀에 무엇을 적을 수 있는지 정한 규격입니다. 기준은 C#의 숫자 리터럴 문법이고, 접미사만 빼놓았습니다.
1. 문제 — 표기가 .NET 파서로 정의되어 있던 것
이 규격이 생기기 전, 숫자 셀이 받는 표기는 NumberStyles 두 줄이 전부였습니다.
| 타입 | 스타일 |
|---|---|
int · bigint | Integer | AllowThousands |
float · double | Float | AllowThousands |
여기에 0x·0b를 따로 처리하는 코드가 붙어 있었고, bitset은 자체 파서를 가지고 있었습니다.
표기의 목록이 어디에도 적혀 있지 않고 세 곳에 흩어져 있는 상태입니다. 결과가 셋입니다.
| 증상 | 예 |
|---|---|
| 자릿수 구분자가 없음 | 0b1010_1010을 적을 수 없어 비트 패턴이 한 덩어리로 붙습니다 |
| 정수와 실수의 표기가 갈림 | 1e3이 double 컬럼에서는 1000이고 int 컬럼에서는 오류입니다 |
| 규격이 프레임워크의 것 | .NET이 무엇을 받는지가 곧 규격이므로, 무엇이 되는지 물으면 문서가 아니라 코드를 읽어야 합니다 |
2. 결정 — C#의 리터럴 문법
결정. 숫자 셀의 표기는 C#의 숫자 리터럴 문법을 따릅니다. 접미사는 없습니다.
기준을 새로 만들지 않고 가져온 이유가 둘입니다.
- 이미 알려져 있습니다. 이 도구를 쓰는 사람은 대부분 코드도 읽으며,
1_000_000과0xFF_FF가 무엇인지 설명할 필요가 없습니다. - 경계 사례가 이미 정해져 있습니다.
1__0은 되고1_.0은 안 되는 것 같은 판정은 새로 정하면 반드시 빠뜨리는 곳이 생기고, C#은 그 답을 전부 가지고 있습니다.
접미사(1.5f·100L·0xFFu)를 빼는 이유는 컬럼의 타입 행이 이미 그것을 말하기 때문입니다.
float 컬럼의 1.5f에서 f는 아무것도 더하지 않고, 타입 행과 어긋난 접미사(int 컬럼의
1.5f)는 어느 쪽을 따를지 물음만 만듭니다.
3. 자릿수 구분자 _
_는 숫자와 숫자 사이에만 놓습니다. 연속해서 놓아도 되고, 몇 개를 놓아도 값이 달라지지
않습니다 — 읽을 때 제거됩니다.
| 적은 것 | 결과 |
|---|---|
1_000_000 | 1000000 |
1__0 | 10 |
0xFF_FF | 65535 |
0b_1010_1010 | 170 — 진법 접두 바로 뒤는 허용합니다 |
3.141_592 | 3.141592 |
1_0.0_1e1_0 | 10.01e10 |
_1000 | 오류 — 앞에 숫자가 없습니다 |
1000_ | 오류 — 뒤에 숫자가 없습니다 |
1_.0 · 1._0 | 오류 — 소수점에 붙습니다 |
1e_5 · 1_e5 | 오류 — 지수 기호에 붙습니다 |
0x_ | 오류 — 숫자가 하나도 없습니다 |
판정은 한 줄입니다 — _를 건너뛰고 좌우를 봤을 때 양쪽 모두 숫자여야 합니다. 진법 접두
바로 뒤가 유일한 예외이고, 그것도 C#의 규칙 그대로입니다.
4. 천 단위 구분자와 섞지 않는 것
1,000,000은 이 규격 이전부터 받아 왔고 계속 받습니다. 디자이너가 큰 수를 그렇게 적기
때문입니다. 파싱이 언제나 InvariantCulture이므로 ,가 소수점이 되는 일은 없습니다.
,와 _를 한 셀에 함께 적으면 오류입니다. 1,000_000이 뜻하는 값은 애매하지 않지만 —
1000000입니다 — 두 표기를 함께 쓴 셀은 적은 사람이 무엇을 하려던 것인지가 애매합니다. 값이
아니라 의도가 갈리는 자리이므로 오류로 둡니다.
5. 정수 컬럼의 지수 표기 — C#과 다른 한 곳
정수 컬럼도 지수 표기를 받습니다. 단 그 값이 정수여야 합니다.
C#에서는 int x = 1e3;이 오류입니다. 1e3은 double 리터럴이고, 정수 자리에 놓으려면 캐스트를
적어야 합니다. 시트에서는 그렇게 두지 않습니다.
| 적은 것 | int 컬럼 |
|---|---|
1e3 | 1000 |
1.5e3 | 1500 |
1E+15 | 1000000000000000 |
1e-3 | 오류 — 정수가 아닙니다 |
1.5 | 오류 — 정수가 아닙니다 |
이유가 둘입니다.
- 시트가 그렇게 적힙니다. 엑셀은 큰 수를
1E+15로 표시하고, 그 표시가 텍스트로 나온 셀은 사람이 손으로 적은 것이 아닙니다. 정수 컬럼이 그것을 거부하면 셀을 고쳐야 하는데, 고칠 것이 없는 셀입니다. - 캐스트를 적을 자리가 없습니다. C#이
1e3을 거부하는 것은 정밀도가 조용히 깎이는 일을 막기 위해서이고, 그 자리에는 「알고 있다」를 적을 문법이 있습니다. 타입 행에는 그것을 적을 자리가 없으므로, 정밀도가 깎이면 오류로 대신합니다.
값은 자릿수를 옮겨서 구합니다. double을 거치지 않으므로 반올림이 끼어들지 않습니다 —
1e3은 "1"의 소수점을 세 자리 옮긴 "1000"이고, 버려지는 자리에 0이 아닌 숫자가 하나라도
있으면 그 자리에서 오류입니다.
6. 진법 리터럴
0x·0b는 이 규격 이전부터 숫자 타입 전체가 받았습니다(비트셋 3절). 달라지는
것은 그 안에 _가 들어간다는 것 하나입니다.
| 성질 | 그대로 |
|---|---|
| 받는 타입 | int · bigint · float · double · bitset |
| 밑수는 표기이지 타입이 아님 | 0xFFFFFFFF는 int 컬럼에서 넘칩니다. 10진수로 적었을 때와 같습니다 |
| 실수는 정확한 값까지 | float은 2^24, double은 2^53을 넘으면 오류입니다 |
0x는 16자리, 0b는 64자리까지 | _는 세지 않습니다 |
16진 실수(0x1.8p3)는 받지 않습니다. C#에도 없습니다.
7. bitset이 _를 받게 되는 것
이전 규격은 bitset이 _를 거부한다고 적었고, 그것이 이 개정에서 뒤집힙니다.
| 판단 | 그때 | 지금 |
|---|---|---|
0b1010_1010 | 오류 | 170 |
뒤집는 이유. _를 거부한 근거는 「비트 패턴에 뜻이 없는 표기」였는데, 자릿수 구분자는
bitset에서 가장 쓸모가 있는 표기입니다. 8비트씩 끊어 적은 마스크는 붙여 적은 것보다 읽기
쉽고, 그 표기를 코드에서는 쓰면서 시트에서는 못 쓸 이유가 없습니다.
나머지 거부는 그대로입니다. 부호(-1) · 천 단위 구분자(1,000) · 소수점(1.0을 포함) ·
지수(1e3)는 계속 오류입니다. 이것들은 값을 두 가지로 읽을 수 있게 만드는 표기이고, _는
그렇지 않습니다 — 받아들이는 범위만 넓어지고 잘못된 값이 조용히 들어갈 자리가 생기지
않습니다.
8. 받지 않는 것
| 표기 | 이유 |
|---|---|
접미사 f · d · m · L · u | 타입 행에 이미 타입이 적혀 있습니다 |
16진 실수 0x1.8p3 | C#에 없습니다 |
8진 0o777 · 0777 | C#에 없습니다. 앞의 0은 값을 바꾸지 않습니다 |
뒤에 붙은 부호 1000- | 부호는 앞에만 적습니다. .NET의 후행 부호 스타일을 켜지 않습니다 |
Infinity · NaN | 데이터 파일에 실을 수 있지만 시트에 적을 표기를 주지 않습니다 |
9. 적용 범위
| 자리 | 적용 |
|---|---|
int · bigint · float · double 셀 | 전부 |
bitset 셀 | _와 진법. 나머지 거부는 유지 |
| 배열 원소 | 각 원소가 하나의 리터럴입니다 |
| 합성 값의 성분 | (0xFF, 0x8_0, 0x40) — 성분은 자기 타입의 표기를 읽습니다 |
foreign 참조 키 | 아닙니다. 참조 키는 대상 테이블의 키 값이고, 그 표기는 대상 컬럼의 타입이 정합니다 |
| enum 라벨의 값 | 아닙니다. 시트가 아니라 선언 자리입니다 |