NTP 타임스탬프에 Decimal Fractional Part에 대한 의문점 해소

int tmp = 0;
double result = 0;
for (int i=0; i<32; i++)
{
	tmp = aa >> (31 - i);
	if (tmp > 0)
	{
		result +=  (double)1.0 / Math.Pow(2, (i+1));
	}
}
result *= 1000;

타임스탬프에 Decimal Fractional Part를 구할때 위와 같은 코드로 구해야 생각했는데 인터넷 검색해보면 다른 사람들은 대부분 아래와 같은 방식을 사용합니다.

ulong milliseconds = intpart * 1000 + (fractpart * 1000) / 0x100000000L;

fractpart에 1000을 곱하는건 밀리세컨드로 바꾸는건데 0x100000000L로 나누는건 뭔지 이해를 못했습니다.

결론만 말하자면 저 0x100000000L은 >> (4*8) 하라는 비트연산입니다.

예를 들어 설명해보죠.
0.34를 비트로 풀어보면

0.34*2 = 0.68 …0
0.68*2 = 1.36 …1
0.36*2 = 0.72 …0
0.72*2 = 1.44 …1

0.44*2 = 0.88 …0
0.88*2 = 1.76 …1
0.76*2 = 1.52 …1
0.52*2 = 1.04 …1

0.04*2 = 0.08 …0
0.08*2 = 0.16 …0
0.16*2 = 0.32 …0
0.32*2 = 0.64 …0

0.64*2 = 1.28 …1
0.28*2 = 0.56 …0
0.56*2 = 1.12 …1
0.12*2 = 0.24 …0

0.24*2 = 0.48 …0
0.48*2 = 0.96 …0
0.96*2 = 1.92 …0
0.92*2 = 1.84 …1

0.84*2 = 1.68 …1
0.68*2 = 1.36 …1
0.36*2 = 0.72 …0

0.72*2 = 1.44 …1

0.44*2 = 0.88 …0
0.88*2 = 1.76 …1
0.76*2 = 1.52 …1
0.52*2 = 1.04 …1

0.04*2 = 0.08 …0
0.08*2 = 0.16 …0
0.16*2 = 0.32 …0
0.32*2 = 0.64 …0

즉 0101 0111 0000 1010 0001 1101 0111 0000 입니다.

Calc

계산하기 편하게 계산기를 뚜두려봅시다.
여기다가 1000을 곱하면 (세컨드에서 밀리세컨드로 변환하니)

Calc

그럼 2진수로 0001 0101 0011 1111 1111 1000 0010 1111 1101 1000 0000
이 나오는데 여기서 >>(4*8) 를 해주면 0001 0101 0011 이게 남습니다.

여기서 의문 왜 32비트를 시프팅 해주냐
제 추측은 Fixed Point를 32:32으로 했으니 *1000을 함으로써 소수점 이하 3자리가 정수 부분으로 올라왔으니 소수부분이었던 하위 32비트를 날리는걸로 생각합니다.

Calc

339가 나옵니다.
0.34*1000했으니 340나와야 하는거 아니냐고요.

예전에 작성한 글에 말했듯이 컴터에서 소수점 처리에는 오차가 있습니다.
32비트 말고 더 길게 늘리면 좀 더 정확한 수치가 나올텐데 뭐 어차피 컴터 시계 세팅하는데 1밀리세컨드까지 고집할 필욘 없겠죠.

이렇게 의문점 하나 해소!


Comments

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다