icon안동민 개발노트

복잡한 수학 연산을 수행하는 함수 라이브러리 구현


실습 개요

 이번 실습에서는 6장에서 학습한 함수 관련 개념들을 종합적으로 적용하여 복잡한 수학 연산을 수행하는 함수 라이브러리를 구현해보겠습니다.

 이를 통해 함수의 정의와 호출, 함수 오버로딩, 인라인 함수, 기본 매개변수, 재귀 함수 등의 개념을 실제로 적용해볼 수 있습니다.

라이브러리 요구사항

  1. 기본적인 사칙연산 함수 구현 (함수 오버로딩 활용)
  2. 거듭제곱 함수 구현 (재귀 함수 활용)
  3. 팩토리얼 계산 함수 구현 (재귀 함수와 반복문 버전)
  4. 삼각함수 계산 함수 구현 (기본 매개변수 활용)
  5. 수학 상수(π, e) 제공 (인라인 함수 활용)

라이브러리 구조

 먼저, 수학 라이브러리를 위한 헤더 파일을 만들어봅시다.

// math_library.h
#ifndef MATH_LIBRARY_H
#define MATH_LIBRARY_H
 
#include <cmath>
 
namespace math_lib {
 
// 사칙연산 함수 (함수 오버로딩)
int add(int a, int b);
double add(double a, double b);
int subtract(int a, int b);
double subtract(double a, double b);
int multiply(int a, int b);
double multiply(double a, double b);
double divide(double a, double b);
 
// 거듭제곱 함수 (재귀)
double power(double base, int exponent);
 
// 팩토리얼 함수 (재귀와 반복)
unsigned long long factorial_recursive(int n);
unsigned long long factorial_iterative(int n);
 
// 삼각함수 (기본 매개변수)
double sin(double angle, bool use_degrees = false);
double cos(double angle, bool use_degrees = false);
double tan(double angle, bool use_degrees = false);
 
// 수학 상수 (인라인 함수)
inline double pi() { return 3.141592653589793; }
inline double e() { return 2.718281828459045; }
 
} // namespace math_lib
 
#endif // MATH_LIBRARY_H

함수 구현

 이제 각 함수를 구현해봅시다.

// math_library.cpp
#include "math_library.h"
 
namespace math_lib {
 
// 사칙연산 함수 구현
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
int subtract(int a, int b) { return a - b; }
double subtract(double a, double b) { return a - b; }
int multiply(int a, int b) { return a * b; }
double multiply(double a, double b) { return a * b; }
double divide(double a, double b) {
    if (b == 0) throw std::runtime_error("Division by zero");
    return a / b;
}
 
// 거듭제곱 함수 구현 (재귀)
double power(double base, int exponent) {
    if (exponent == 0) return 1;
    if (exponent < 0) return 1 / power(base, -exponent);
    return base * power(base, exponent - 1);
}
 
// 팩토리얼 함수 구현 (재귀)
unsigned long long factorial_recursive(int n) {
    if (n <= 1) return 1;
    return n * factorial_recursive(n - 1);
}
 
// 팩토리얼 함수 구현 (반복)
unsigned long long factorial_iterative(int n) {
    unsigned long long result = 1;
    for (int i = 2; i <= n; ++i) {
        result *= i;
    }
    return result;
}
 
// 삼각함수 구현
double sin(double angle, bool use_degrees) {
    if (use_degrees) angle = angle * pi() / 180.0;
    return std::sin(angle);
}
 
double cos(double angle, bool use_degrees) {
    if (use_degrees) angle = angle * pi() / 180.0;
    return std::cos(angle);
}
 
double tan(double angle, bool use_degrees) {
    if (use_degrees) angle = angle * pi() / 180.0;
    return std::tan(angle);
}
 
} // namespace math_lib

라이브러리 사용 예시

 이제 우리가 만든 라이브러리를 사용해봅시다.

#include <iostream>
#include "math_library.h"
 
int main() {
    using namespace math_lib;
 
    // 사칙연산 테스트
    std::cout << "5 + 3 = " << add(5, 3) << std::endl;
    std::cout << "5.5 - 2.2 = " << subtract(5.5, 2.2) << std::endl;
    std::cout << "4 * 6 = " << multiply(4, 6) << std::endl;
    std::cout << "10 / 3 = " << divide(10.0, 3.0) << std::endl;
 
    // 거듭제곱 테스트
    std::cout << "2^5 = " << power(2, 5) << std::endl;
 
    // 팩토리얼 테스트
    std::cout << "5! (recursive) = " << factorial_recursive(5) << std::endl;
    std::cout << "5! (iterative) = " << factorial_iterative(5) << std::endl;
 
    // 삼각함수 테스트
    std::cout << "sin(30°) = " << sin(30, true) << std::endl;
    std::cout << "cos(π/3) = " << cos(pi() / 3) << std::endl;
 
    // 수학 상수 테스트
    std::cout << "π = " << pi() << std::endl;
    std::cout << "e = " << e() << std::endl;
 
    return 0;
}

실습

  1. 로그 함수(자연로그와 상용로그)를 추가로 구현하세요.
  2. 복소수 연산을 지원하는 함수들을 추가하세요. (힌트 : std::complex 사용)
  3. 행렬 연산(덧셈, 곱셈, 전치)을 수행하는 함수들을 구현하세요.
  4. 모든 함수에 대해 적절한 예외 처리를 추가하세요. (예 : 0으로 나누기, 음수 팩토리얼 등)
  5. 성능 최적화를 위해 메모이제이션 기법을 사용하여 피보나치 수열 함수를 구현하세요.

생각해보기

 1. 함수 오버로딩 vs 템플릿 : 이 라이브러리에서 어떤 접근 방식이 더 적합할까요? 각각의 장단점을 논의해보세요.

 2. 재귀 함수와 반복문 : 팩토리얼과 거듭제곱 함수에서 어떤 구현이 더 효율적인가요? 시간 복잡도와 공간 복잡도 측면에서 분석해보세요.

 3. 인라인 함수의 사용 : π와 e 같은 상수를 제공할 때 인라인 함수를 사용하는 것의 장단점은 무엇인가요? 다른 방법은 없을까요?

 4. 예외 처리 vs 오류 코드 : 수학 라이브러리에서 오류 상황(예 : 0으로 나누기)을 처리할 때 어떤 방식이 더 적절할까요?

 5. 부동 소수점 연산의 정확성 : 현재 구현에서 발생할 수 있는 부동 소수점 오차의 문제점과 이를 개선할 수 있는 방법에 대해 논의해보세요.


 참고자료

  • C++ 표준 라이브러리의 <cmath> 헤더 : C++ <cmath>
  • "Numerical Recipes in C++" by William H. Press et al.
  • "C++ Templates : The Complete Guide" by David Vandevoorde, Nicolai M. Josuttis, and Douglas Gregor
  • "Optimizing C++" by Steve Heller
  • C++ Core Guidelines : F : Functions