3개의 명령어로 윤년 여부를 확인하는 방법

1 month ago 10

  • 3개의 CPU 명령어만으로 윤년을 판별하는 함수를 구현함
  • 이 방법은 비트 연산과 곱셈을 이용하여 전통적 분기 없이 동작함
  • Z3와 같은 SMT Solver로 매직 상수를 찾아낸 과정이 설명됨
  • 이 방식은 0~102499년 범위에서 정확함을 갖추고 있음
  • 벤치마크 결과 기존 방법 대비 3.8배 가량 빠른 성능을 보임

개요 및 문제 제시

  • 0에서 102499년 사이의 년도에 대해, 단 3개의 CPU 명령어만 사용하여 윤년을 판별하는 함수가 소개됨
    • 사용되는 실제 함수는 ((y * 1073750999) & 3221352463) <= 126976 구조임
  • 이 비트 트위들링(bit-twiddling) 기법의 원리와 동작, 실질적인 효용성에 대해 설명함

전통적인 윤년 판별 방법

  • 보통 윤년 판별은 나눗셈(modulo)와 조건분기로 구현
    • 4로 나누어 떨어지는지, 100으로 안 나누어지는지, 400으로 나누어 떨어지는지 검사함
    • 예시 코드: if ((y % 4) != 0) return false if ((y % 100) != 0) return true if ((y % 400) == 0) return true return false

표준 접근 방식의 최적화

  • (y % 100) 검사를 (y % 25)로, (y % 400) 검사를 (y % 16)으로 대체 가능함을 설명함

    • 그 이유는 앞서 4로 이미 나누었으므로, 25 및 16 곱셈 관계로 변경 가능함
  • (y % 25) 연산을 나눗셈 없이 곱셈과 비교로 변환하는 마법 상수 사용법을 설명함

    • 예: x * 3264175145 > 171798691로 변환 가능
  • 비트마스크를 더해 (y & 3)이나 (y & 15)로 4 또는 16의 나눗셈 대체 가능함

  • 컴파일러는 이러한 변환을 자동화하지만, 직접 다른 언어에서 활용할 수도 있음

분기 없는(branchless) 구현법

  • 분기가 없는 형태로도 변환 가능함을 제시함: return !(y & ((y % 25) ? 3 : 15))
  • 이러한 방식은 코드 골프(coding golf)와 같이 코드 길이 줄이기에 적합함

매직 상수 찾기: 비트 트위들링 접근법

  • 윤년 판별식을 더욱 간단하게 만들기 위해 조합적 탐색과 SMT SolverZ3 활용함
    • 형태: ((y * f) & m) <= t
  • 요구 조건을 만족시키는 상수 f, m, t를 Z3로 탐색
    • 0~102499 범위에서 정확한 결과를 얻을 수 있는 값 발굴
    • 최종 결과가 (y * 1073750999) & 3221352463 <= 126976임

함수 원리 및 내부 구조 해설

  • 상수를 이진수로 분석하여, 세 개의 주요 비트 영역 A, B, C로 구분함

    • 각 영역의 비트 상태에 따라 윤년 판별의 3가지 조건을 포괄함
  • 함수의 논리적 분해:

    • A 영역: (y % 4) != 0 여부를 비롯한 4의 배수 조건 확인
    • B 영역: (y % 100) != 0여부를 다양한 패턴(예: 뒷자리 14, 57, 71 등)로 필터링
    • C 영역: (y % 16) == 0의 즉, 16의 배수 확인
  • 곱셈이 실제로 나머지 계산 없이 다양한 조건을 결합해내는 원리 해설

    • 매직 상수를 곱할 때, 100의 배수 등에서 특이적인 비트 패턴이 형성됨
    • 추가적으로, 곱셈 오차와 여러 자리 숫자 패턴이 등장하는 수학적 내부 구조 분석이 포함됨

추가 범위 및 비트폭 확장 가능성

  • 64비트로 확장할 경우 적합한 매직 상수 조합 탐색법도 제시
    • f, m, t값을 다양하게 바꿔보며 최장 범위를 찾을 수 있음
    • StackExchange에서도 최적 조합 및 Z3 활용 증명 사례가 있음

벤치마크 및 실제 성능 비교

  • 벤치마크 결과:
    • 2025년 등 예측 가능한 값에서는 0.65~0.69ns로 차이가 거의 없음
    • 무작위 입력시 is_leap_year_fast가 3.8배 정도 빠른 성능 확보
    • 입력 패턴에 따라 분기(branching) 방식이 예측 불가할 때 상당한 이점이 있음

결론 및 실제 적용 여부

  • 실제 응용에서 값이 예측가능할 때는 이득 적음, 하지만 대량의 무작위 데이터 상황에선 매우 유용
  • 실제 파이썬이나 C# 등 표준 라이브러리 교체 시에는 현실적인 전체 프로그램 벤치마크가 필요함
  • 아이디어와 구현 방법 자체가 흥미롭고, 특정 상황에서는 성능상 매력적인 솔루션임

Read Entire Article