정수형

정수 자료형의 종류와 크기는 다음과 같다.

자료형 설명 크기(byte) 범위
byte 부호 없는 정수 1 byte 0 ~ 255
sbyte 정수 1 byte -128 ~ 127
short 정수 2 byte -32,768 ~ 32,767
ushort 부호 없는 정수 2 byte 0 ~ 65,535
int 정수 4 byte -2,147,483,648 ~ 2,147,483,647
uint 부호 없는 정수 4 byte 0 ~ 4,294,967,295
long 정수 8 byte -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
ulong 부호 없는 정수 8 byte 0 ~ 18,446,744,073,709,551,615
char 유니코드 문자 2 byte
using System;

namespace IntegralTypes
{
    class MainApp
    {
        static void Main(string[] args)
        {
            sbyte sb = -10;
            byte b = 40;
            Console.WriteLine($"sb = {sb}, b = {b}");

            short s = -30000;
            ushort us = 60000;
            Console.WriteLine($"s = {s}, us = {us}");

            int i = -10000000;
            uint ui = 300000000;
            Console.WriteLine($"i = {i}, ui = {ui}");

            long l = -500000000000;
            ulong ul = 2000000000000000000;
            Console.WriteLine($"l = {l}, ul = {ul}");
        }
    }
}
sb = -10, b = 40
s = -30000, us = 60000
i = -10000000, ui = 300000000
l = -500000000000, ul = 2000000000000000000

unsigned 정수형

만약 음수를 저장할 필요가 없다면, 같은 공간으로 2배의 범위를 지정할 수 있는 unsigned 정수형을 고려해 볼 수 있다.

signed 방식은 가장 왼쪽의 비트를 이용해 부호 표시를 하지만, unsigned 방식은 가장 왼쪽의 비트도 범위를 표현하는데 사용한다.

image.png

signed 방식에서는 가장 왼쪽의 비트가 0 이면 양수, 1 이면 음수를 표현한다.

using System;

namespace IntegralTypes
{
    class MainApp
    {
        static void Main(string[] args)
        {
            byte a = 0b10010100;   // 148 = (128 + 16 + 4)
            Console.WriteLine($"a = {a}");

            sbyte b = (sbyte)a;    // -108 = (-128 + 16 + 4)
            Console.WriteLine($"b = {b}");
        }
    }
}
a = 148
b = -108

2의 보수법

컴퓨터에서 음의 정수를 표현할 때에는 2의 보수를 취해야 한다. 2의 보수법은 다음과 같은 과정을 거친다.

  1. 1의 보수를 취한다.
  2. 1을 더한다.

Untitled

정수 +5-5 를 더했을 때 0 이 되는 것을 확인할 수 있다.

Untitled

오버플로 & 언더플로

변수는 데이터를 담는 그릇과 같다. 그릇에 용량 이상의 물을 담으면 넘치는 것처럼, 변수에도 데이터 형식의 크기를 넘어선 값을 담으면 넘친다. 이러한 현상을 오버플로(Overflow) 라고 한다.

byte 형 변수 abyte 가 담을 수 있는 최대값을 저장하고 a1 을 더해보도록 하겠다.

using System;

namespace IntegralTypes
{
    class MainApp
    {
        static void Main(string[] args)
        {
            byte a = byte.MaxValue;
            Console.WriteLine($"a = {a}");

            a = (byte)(a + 1);
            Console.WriteLine($"a = {a}");
        }
    }
}
a = 255
a = 0

오버플로된 변수 a 는 byte 가 가질 수 있는 최저값 0 을 갖는다.

이는 수를 2진수로 바꿔보면 쉽게 이해할 수 있다. byte 의 최대값은 255 이며, 255는 2진수로 바꾸면 1111 1111 이다.

image.png

여기에 1 을 더하면 1 0000 0000 이 된다. 그러나 byte 는 8개의 비트만 담을 수 있으므로 넘쳐 흐른 왼쪽의 비트는 버리고 오른쪽 8개 비트 0000 0000 만 보관하게 된다.

image.png

위 처럼 각 데이터 형식의 최대값을 넘어가는 데이터를 저장할 때는 오버플로가 일어나지만 최저값보다 작은 데이터를 저장하면 언더플로(Underflow) 가 일어난다.

byte 형식 변수에 -1 을 담으려하면 실제로는 255 가 저장된다.

using System;

namespace IntegralTypes
{
    class MainApp
    {
        static void Main(string[] args)
        {
            sbyte a = -1;
            Console.WriteLine($"a = {a}");

            byte b = (byte)a;
            Console.WriteLine($"b = {b}");
        }
    }
}
a = -1
b = 255

실수형 (부동 소수점형)

실수 자료형의 종류와 크기는 다음과 같다.

데이터 형식 설명 크기(byte) 범위
float 단일 정밀도 부동 소수점 형식
(7개의 자릿수만 다룰 수 있음) 4 btye -3.402823 * 10^(38) ~
3.402823 * 10^(38)
double 복수 정밀도 부동 소수점 형식
(15~16개의 자릿수를 다룰 수 있음) 8 byte -1.79769313486232 * 10^(308) ~ 1.79769313486232 * 10^(308)
using System;

namespace FloatingPoint
{
    class MainApp
    {
        static void Main(string[] args)
        {
            float a = 3.14159265358979323846f;
            Console.WriteLine(a);

            double b = 3.14159265358979323846;
            Console.WriteLine(b);
        }
    }
}
3.1415927
3.141592653589793

float 형 선언 시 숫자 뒤에 f 를 붙여야 하며, floatdouble 은 자신의 가수부가 담을 수 있는 부분까지만 저장하고 나머지는 버린다.

부동소수점 원리

C#의 실수형은 IEEE 754 라는 표준 알고리즘에 기반한 데이터 형식으로 실수를 저장하기 위해 소수점 . 을 유동적으로 움직여서 표현한다.

이를 위해 비트 공간을 아래와 같이 사용한다.

Untitled

float 형 맨 왼쪽 1비트를 부호, 그 다음 8비트를 지수, 그 다음 23비트를 가수부로 사용한다. double 형 맨 왼쪽 1비트를 부호, 그 다음 11비트를 지수, 그 다음 52비트를 가수부로 사용한다.

이 과정에서 저장 공간은 한정되어 있기 때문에 소수점 절삭으로 인한 오차가 발생한다. 따라서 실수형은 항상 '근삿값'이라는 것을 기억해야 한다.

decimal 형식

decimal 도 실수를 다루는 데이터 형식으로 위의 부동 소수점과는 다른 방식으로 소수를 다루며 정밀도가 훨씬 높다.

데이터 형식 설명 크기 (btye) 범위
decimal 29자리 데이터를 표현할 수 있는
소수 형식 16 byte 약 ± 1.0 * 10^(-28) ~ ± 7.9 * 10^28
using System;

namespace Decimal
{
    class MainApp
    {
        static void Main(string[] args)
        {
            float a = 3.141592653589793238462643383279f;
            Console.WriteLine(a);

            double b = 3.141592653589793238462643383279;
            Console.WriteLine(b);

            decimal c = 3.141592653589793238462643383279m;
            Console.WriteLine(c);
        }
    }
}
3.1415927
3.141592653589793
3.1415926535897932384626433833

decimal 형 선언 시 숫자 뒤에 m 을 붙여야 한다.


문자형 & 문자열

문자형

char 형식은 정수를 다루는 데이터 형식이지만, 수가 아닌 'a', 'b', 'c' 와 같은 문자 데이터를 다룬다. 작은 따옴표 '' 로 문자를 감싸줘야 한다.

using System;

namespace Char
{
    class MainApp
    {
        static void Main(string[] args)
        {
            char a = 'H';
            char b = 'E';
            char c = 'L';
            char d = 'L';
            char e = 'O';

            Console.Write(a);
            Console.Write(b);
            Console.Write(c);
            Console.Write(d);
            Console.Write(e);
        }
    }
}
HELLO

문자열

string 형식은 여러 개의 문자 형식을 하나로 묶어 처리하며, 정해진 크기나 담을 수 있는 데이터의 범위가 따로 정해져 있지 않다.

큰 따옴표 "" 로 문자열 데이터를 감싸줘야 한다.

using System;

namespace String
{
    class MainApp
    {
        static void Main(string[] args)
        {
            string a = "HELLO";

            Console.Write(a);
        }
    }
}
HELLO

\\n 을 통해 줄 바꿈을 할 수 있으며, 큰 따옴표 3개를 붙여 쓰면 """...""" 여러 줄로 이어진 문자열 리터럴을 만들 수 있다.

using System;

namespace MultiLine
{
    class MainApp
    {
        static void Main(string[] args)
        {
            string a = "Hello\\nWorld!\\n";

            Console.WriteLine(a);

            string b = """
                동해물과 백두산이
                마르고 닳도록
                하느님이 보우하사
                우리나라 만세             
                """;

            Console.WriteLine(b);
        }
    }
}
Hello
World!

동해물과 백두산이
마르고 닳도록
하느님이 보우하사
우리나라 만세

문자열 다루기

string 형식은 문자열을 담는 역할을 할 뿐 아니라, 문자열을 가공하기 위한 다양한 기능도 함께 제공한다.