먼저 파이썬 인터프리터를 실행하여 0.3 - 0.2 - 0.1 의 계산 결과를 알아 보았습니다.

>>> 0.3 - 0.2 - 0.1
-2.7755575615628914e-17

계산 결과가 왜 0이 아닌 엉뚱한 값 -2.7755575615628914e-17 이 될까요?

 

IEEE754 에 대해서는 관련되는 두 문서

    [1]  Double-precision floating-point format  

    [2]  Floating Point Arithmetic: Issues and Limitations 

를 참고하기 바랍니다.

 

NET Framework 4.6.x 이상에서 사용가능한 NET용 분수 계산 라이브러리를 소개합니다.

(소스 수정 없이 NET Core 5.0 이상에서도 사용 가능합니다.)

(Mac OS 에서도 FractionLib 를 빌드하여 쓸 수 있습니다만, UnitTest 프로젝트는 새로 작성해야 합니다.)

FractionLib project  에서 내려받은 프로젝트 파일을

Vusual Studio 2019 이상에서 빌드하여 생성된 FractionLib.dll을,

다른 프로젝트에서 라이브러리로 참조 사용하면 IEEE754 의 배정밀도 부동소숫점수를

분수로 받아들여 보다 정확한 수치 계산을 할 수 있습니다.

내부적으로 C#의 표준 큰 정수 BigInteger를 이용하므로 System.Numerics,dll을

함께 참조 추가하면 더 좋습니다.

FractionLib,dll을 사용할 수 있는 언어로는 C# 언어 외에 F#, Visual Basic, IronPython, C++/CLI

언어들도 가능합니다.

사용 예제는 위 프로젝트의 Examples 폴더에 있습니다.

 

부동소수점수의 뺄셈 계산

      0.3 - 0.2 - 0.1

의 계산 결과는 당연히 0이 되어야 함에도 불구하고 

IEEE 754에서 정한 부동소수점수의 한계(mantissa, 유효숫지 부분의 한계)

때문에 0.3 - 0.2 - 0.1 의 계산 결과가 0이 아닌 적당한 근사값로 계산되며,

그 역수는  -1.0E20 보다 큰 음수로 계산됩니다.

그러나 FractionLib.dll 을 사용하여

 

using knumerics;

    ......................
    ......................

    MyFraction a3 = new MyFraction(0.3);
    MyFraction a2 = new MyFraction(0.2);
    MyFraction a1 = new MyFraction(0.1); 
    MyFraction zero = new MyFraction(0, 1);
    
    MyFraction result = a3 - a2 - a1;
    Console.WriteLine("result == zero ? {0}", result == zero);

 

로 작상하면 0.3 - 0.2 - 0.1의 계산 결과가 정확히 0이 됩니다.

 

참고로, 온라인 상에서 Python 언어, C# 언어, C 언어로 각각 테스트해 보았습니다.

 

Python 언어로 0.3 - 0.2 - 0.1 계산하기

 

C# 언어로 0.3 - 0.2 - 0.1 계산하기

 

 

C 언어로 0.3 - 0.2 - 0.1 계산하기

 

Posted by Scripter
,

gmplib 의 현재 최신 릴리즈는 2020년 11월 14일에 출시된 gmplib 6.2.1 이다.

우선 이곳에서 압축 파일을 하나 다운로드한다.

여기서는 gmp-6.2.1.tar.xz 을 다운로드한 것으로 간주하고 설치 과정을 소개한다.

우선 tar 명령으로 다운로드한 파일의 압축을 해제한다.

(아래에서 $ 는 쉘 프롬프트이므로 입력하지 않아먀 한다.)

$ tar Jxvf gmp-6.2.1.tar.gz

위 명령에 의하여 현제 폴더에 gmp-6.2.1 이라는 폴더가 생기면서

압축된 것들이 이 폴더 아래에 해제된다.

이제 cd 명령으로 그 하위 폴더로 이동한다.

$ cd gmp-6.2.1

configure 명령으로 설치하기 전 설정을 한다.

여기서 옵션 --enable-cxx 는 #include <gmpxx.h> 구문으로

c++ 언어를 사용하기 위함이다.

./configure --enable-cxx

이제 make 명령으로 빌드한다.]

$ make

설치 전에 빌드한 것을 체크한다.

make check

빌드된 것을 /usr/local 폴더에 설치한다.

이 풀더에는 관리자 권한으로 쓰기 작업해야 하므로 sudo 명령이 필요하다.

(사용자의 개인 폴더에 설치할 때는 sudo 명령이 없어도 된다.)

$ sudo make install

설치 과정 중에 파생된 잡다한 파일들을 제거한다.


make clean

c++ 용 에제 소스를 작성하고 more 명령으로 확인한다.

more test-gmp.cc
#include <iostream>
#include <gmpxx.h>

using namespace std;

int main (void)
{
  mpz_class a, b, c;

  a = 1234;
  b = "-5678";
  c = a+b;
  cout << "sum is " << c << "\n";
  cout << "absolute value is " << abs(c) << "\n";

  return 0;
}


작성된 위의 소스를 g++ 명령으로 컴파일한다.


g++ -o test-gmp test-gmp.cc -lgmpxx -lgmp

 

컴파일 하여 생성된 파일을 실행한다.


./test-gmp
sum is -4444
absolute value is 4444

 

 

 

Posted by Scripter
,

 

0.3 - 0.2 - 0.1 == 0.0

 

0.3 - 0.2 - 0.1 != 0.0

 

0.3 - 0.2 - 0.1 -eq 0.0

 

0.3 - 0.2 - 0.1 -ne 0.0

 

PowerShell 뿐만 아니라 배정말도 부동소수점수(IEEE754-1987 포맷) 계산에서는 

십진수로 유효수자(sifnificant digits) 개수가 약 15개 밖에 안되므로

모든 소수점수를 근사값으로 계산한다. (mantissa 가 52bit, 부호가 1bit, 지수부가 11bit 로 총 64bit 즉, 8byte)

 

 

 

 



        Byte 7     Byte 6      Byte 5    Byte 4      Byte 3    Byte 2     Byte 1    Byte 0                                                                       Byte 0
       --------   --------   --------   --------   --------   --------   --------   --------
          6            5            4               3           2              1
       32109876   54321098   76543210   98765432   10987654   32109876   54321098   76543210 
       ========   ========   ========   ========   ========   ========   ========   ========
       snnnnnnn   nnnnbbbb   bbbbbbbb   bbbbbbbb   bbbbbbbb   bbbbbbbb   bbbbbbbb   bbbbbbbb
       ========   ========   ========   ========   ========   ========   ========   ========
       S    Exponent           Mantissa
      63[    62 ~ 52 ][         51 ~ 0                                                    ]
      
      
      
                   (Sign) (1.bbbbb....bbbbb)_2 * pow(2, (nnnnnnnnnnn)_2 - 307)
                     +/-     1 bit + 53 bits                22 bits       bias
                            -----------------
                                54 bits
                                   
       -

 

 

Posted by Scripter
,

 

아래의 단순한 코드로 작성되어 gcc 로 컴파일된 실행파일의 콘솔 출력이 비정상인 경우....

 

#include <stdio.h>

int main(void)
{
    int i;

    for (i = 0; i < 5; i++)
    {
        printf("abcd %d\r\n",i);
        Sleep(1000);
    }
    return 0;
}

 

첫째 해결 방법:

    아래의 수정된 코드 처럼 모든 printf.(...);  구문 뒤에 fflush(strout); 를 추가한다.

#include <stdio.h>

int main(void)
{
    int i;

    for (i = 0; i < 5; i++)
    {
        printf("abcd %d\r\n",i);
        fflush(stdout);     // 모든 printf(...); 구문 뒤에 이를 추가한다.
        
        Sleep(1000);
    }
    return 0;
}

 

둘째 해결 방법:

    아래의 수정된 코드 처럼 모든 main() 함수에서

     printf(...);  구문이 시작되기 전에 setvbuf(...);  구문을 한 번만 추가한다.

#include <stdio.h>

int main(void)
{
    int i;

    setvbuf(stdout, (char *)NULL, _IOLBF,0 );

    for (i = 0; i < 5; i++)
    {
        printf("abcd %d\r\n",i);
        Sleep(1000);
    }
    return 0;
}

 

※ 윈도우의 MSYS2 에서 flex & bison 으로 작성된 어플의 printf() 출력이 제대로 안 될 때는

  위의 두 가지 방법 모두 안 되니, 다른 방법으로 해결해여 한다.

 

 

 

Posted by Scripter
,

 

 

아래의 소스는

Coefficients for the Lanczos Approximation to the Gamma Function

의 것을 Microsoft Visual C/C++ 명령줄 컴파일러 cl 로 컴파일되도록 수정한 것이다.

 

// Filename: test_gamma_lanczos.c
//
// Compile: cl test_gamma_lanczos.c /EHsc /utf-8
// Execute: test_gamma_lanczos

#include <stdio.h>
	
#define _USE_MATH_DEFINES // for C
#include <math.h>
	
  #define LG_g 5.0      // Lanczos parameter "g"
  #define LG_N 6        // Range of coefficients i=[0..N]
  const double lct[LG_N+1] = {
     1.000000000190015,
    76.18009172947146,
   -86.50532032941677,
    24.01409824083091,
    -1.231739572450155,
     0.1208650973866179e-2,
    -0.5395239384953e-5
  };

  const double ln_sqrt_2_pi = 0.91893853320467274178;
  const double         g_pi = 3.14159265358979323846;

  double lanczos_ln_gamma(double z)
  {
    double sum;
    double base;
    double rv;
    int i;
    
    if (z < 0.5) {
      return log(g_pi / sin(g_pi * z)) - lanczos_ln_gamma(1.0 - z);
    }
    z = z - 1.0;
    base = z + LG_g + 0.5;  // Base of the Lanczos exponential
    sum = 0;

    for(i=LG_N; i>=1; i--) {
      sum += lct[i] / (z + ((double) i));
    }
    sum += lct[0];
    return ((ln_sqrt_2_pi + log(sum)) - base) + log(base)*(z+0.5);
  }

  double lanczos_gamma(double z)
  {
    return(exp(lanczos_ln_gamma(z)));
  }
  
  int main()
  {
      printf("                        Gamma(0.5)               Log(Gamma(0.5))\n");
      printf("                 ---------------------------------------------------\n");
      printf("  Lanczos Gamma: %22.18f     %22.18f\n", lanczos_gamma(0.5), lanczos_ln_gamma(0.5));
      printf("       Built-in: %22.18f     %22.18f\n", exp(lgamma(0.5)), lgamma(0.5));
      printf("   Use sqrt(PI): %22.18f     %22.18f\n", sqrt(M_PI), log(sqrt(M_PI)));

      return 0;	  
  }

 

 

빌트인 Gamma 함수와 비교하여 12자리 까지만 맞는데,

이는 허용오차가 약 sqrt(PI)*N 이기 때문이다.

여기서 N은 Lanczos 계수의 개수이다, (소스에서는 LG_N+1)

Python 인터프러터를 실행하여 잠깐 그 허용오차를 알아본다.

Python 3.7.5 
>>> import math
>>> math.gamma(0.5)
1.7724538509055159
>>> import math
>>> math.sqrt(math.pi)*7
12.407176956338612

N=7 인 경우, 유효수자의 개수는 약 12개이다.

 

 

 

 

 

 

 

 

 

Posted by Scripter
,

 

Boost Library 1.75.0 을 설치하고

Visual Studio 2019 의 명령행 컴파일러 cl 로 컴파일하였다.

컴파일하기 전에 미리 환경변수 BOOST_LIB_PATH 와 MPIR_LIB_PATH 를 각각

Boost Library 와 MPIR Library 가 있는 경로로 설정해 놓아야 한다.

 

/*
 * Filename: CalculateAreaOfDiskWithBoosLibrary.cpp
 *
 *       Purpose: Test floating point numbers of arbitrary precision.
 *
 * Compile: cl CalculateAreaOfDiskWithBoosLibrary.cpp /I%BOOST_LIB_PATH% /I%MPIR_LIB_PATH% /EHsc /utf-8 MPIR_LIB_PATHmpir.lib
 * Execute: CalculateAreaOfDiskWithBoosLibrary
 *
 * Date: 2021.03.29
 * Revised Author: pkim ((AT)) scripts ((DOT)) pe ((DOT)) kr 
 *
 * See: www.boost.org/doc/libs/1_56_0/libs/multiprecision/doc/html/boost_multiprecision/tut/floats/gmp_float.html
 */
 
#include<iostream>
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/math/constants/constants.hpp>

using boost::multiprecision::cpp_dec_float_50;
using boost::multiprecision::cpp_dec_float_100;
using boost::multiprecision::cpp_dec_float;
using boost::multiprecision::cpp_bin_float_quad;

#include <boost/multiprecision/gmp.hpp>
using namespace boost::multiprecision;

typedef boost::multiprecision::number<boost::multiprecision::cpp_dec_float<40> > flp_type_dec_40;
typedef boost::multiprecision::number<boost::multiprecision::cpp_dec_float<49> > flp_type_dec_49;
typedef boost::multiprecision::number<boost::multiprecision::cpp_dec_float<99> > flp_type_dec_99;
typedef boost::multiprecision::number<boost::multiprecision::cpp_dec_float<101> > flp_type_dec_101;


using namespace std;

template<typename T>
inline T area_of_a_circle(T r)
{
    // pi represent predefined constant having value
    // 3.1415926535897932384...
    using boost::math::constants::pi;
    return pi<T>() * r * r;
}



void testFloatNumbers()
{
	float radius_f = 123.0/ 100;
	float area_f = area_of_a_circle(radius_f);

	double radius_d = 123.0 / 100;
	double area_d = area_of_a_circle(radius_d);

	cpp_dec_float_50 r_mp = 123.0 / 100;
	cpp_dec_float_50 area_mp = area_of_a_circle(r_mp);

	cpp_dec_float_100  r_100 = 123.0 / 100;
	cpp_dec_float_100 area_100 = area_of_a_circle(r_100);


	cpp_bin_float_quad  r_quad= 123.0 / 100;
	cpp_bin_float_quad area_quad = area_of_a_circle(r_quad);


	flp_type_dec_40  r_40 = 123.0 / 100;
	flp_type_dec_40 area_40 = area_of_a_circle(r_40);


	flp_type_dec_49  r_49 = 123.0 / 100;
	flp_type_dec_49 area_49 = area_of_a_circle(r_49);


	flp_type_dec_99  r_99 = 123.0 / 100;
	flp_type_dec_99 area_99 = area_of_a_circle(r_99);


	flp_type_dec_101  r_101 = 123.0 / 100;
	flp_type_dec_101 area_101 = area_of_a_circle(r_101);


      using boost::math::constants::pi;
      cout << "Let be given a circle with radius, r = " << radius_d << endl;
      cout << "Calculate the area of the given circle,\n        Area = PI*r^2 = " << pi<double>() << "*" << radius_d << "^2 = " << pi<double>() << "*" << (radius_d * radius_d) << ",\nin various precisions." << endl << endl;;

	// numeric_limits::digits10 represent the number
	// of decimal digits that can be held of particular
	// data type without any loss.
	
	// Area by using float data type
	cout << " Float: "
		<< setprecision(numeric_limits<float>::digits10)
		<< area_f << endl;

	// Area by using double data type
	cout << "Double: "
		<<setprecision(numeric_limits<double>::digits10)
		<< area_d << endl;


	// Area by using Boost 40 precision Multiprecision
	cout << "Boost Multiprecision  (40 digits): "
		<< setprecision(numeric_limits<flp_type_dec_40>::digits10)
		<< area_49 << endl;

	// Area by using Boost 49 precision Multiprecision
	cout << "Boost Multiprecision  (49 digits): "
		<< setprecision(numeric_limits<flp_type_dec_49>::digits10)
		<< area_49 << endl;

	// Area by using Boost 50 precision Multiprecision
	cout << "Boost Multiprecision  (50 digits): "
		<< setprecision(numeric_limits<cpp_dec_float_50>::digits10)
		<< area_mp << endl;

	// Area by using Boost 99 precision Multiprecision
	cout << "Boost Multiprecision  (99 digits): "
		<< setprecision(numeric_limits<flp_type_dec_99>::digits10)
		<< area_99 << endl;

	// Area by using Boost 100 precidion Multiprecision
	cout << "Boost Multiprecision (100 digits): "
		<< setprecision(numeric_limits<cpp_dec_float_100>::digits10)
		<< area_100 << endl;

	// Area by using Boost 101 precision Multiprecision
	cout << "Boost Multiprecision (101 digits): "
		<< setprecision(numeric_limits<flp_type_dec_101>::digits10)
		<< area_100 << endl;

	// Area by using Boost Quaduple Multiprecision
	cout << "Boost Multiprecision  (Quadruple): "
		<< setprecision(numeric_limits<cpp_bin_float_quad>::digits10)
		<< area_quad << endl;
}


int main()
{ 
	testFloatNumbers();
	return 0;
}


/*
Output:
-------------------------------------------
Let be given a circle with radius, r = 1.23
Calculate the area of the given circle,
        Area = PI*r^2 = 3.14159*1.23^2 = 3.14159*1.5129,
in various precisions.

 Float: 4.75292
Double: 4.752915525616
Boost Multiprecision  (40 digits): 4.752915525615998053187629092943809341311
Boost Multiprecision  (49 digits): 4.752915525615998053187629092943809341310825398145
Boost Multiprecision  (50 digits): 4.7529155256159980531876290929438093413108253981451
Boost Multiprecision  (99 digits): 4.75291552561599805318762909294380934131082539814514244132288567973008216166037385350291572422559648
Boost Multiprecision (100 digits): 4.752915525615998053187629092943809341310825398145142441322885679730082161660373853502915724225596483
Boost Multiprecision (101 digits): 4.7529155256159980531876290929438093413108253981451424413228856797300821616603738535029157242255964827
Boost Multiprecision  (Quadruple): 4.75291552561599805318762909294381
-------------------------------------------
*/

 

 

 

Posted by Scripter
,

 

C 언어로 동적 메모리(dynamic memory)를 할당빋으려면 malloc() 함수나 calloc() 함수를 사용하고, 해제할 때는 free() 함수를 사용한다.

C++ 언에서도 이를 사용해도 되지만 메모리 할당과 관리를 객체의 생성과 소멸 과정 중에 혹은 함수의 호출과 리턴의 과정 중에 프로그래머가 일일이 간섭하려면 귀찮기도 하고, 잠간의 실수로, 심각한 버그가 발생하여 치명적인 결함이 생길 수도 있다.

C++ 언어에서는 배열의 메모리 할당과 해제를 C 언어 보다 좀  더 안전하고 편하게 해 주는

new 타입[] 과  delete[] 포인터변수 형태의  구문이 있다.

예를 들어, 부동소수점수 double 타입의 값을 10개 저장하는 공간을 할당받고 해제하는 구문의 예는 다음과 같다.

 

    double *my_arr = new double[10] { 1, 2, 3 };

    ...........적당히 사용...................

    delete[] my_arr;

 

혹은

 

    double *my_arr = new double[10];

    my_arr[0] = 1;

    my_arr[1] = 2;

    my_arr[2] = 3;

    ...........적당히 사용...................

    delete[] my_arr;

 

그런데 가끔씩 배열의 크기를 변경헤야 하는 경우가 있는데,

이럴 때는 다음 예를 상황에 맞게 수정하여 쓰면 된다.

matrix 형태의 배열을 취급하는 예도 포함되어 있는 소스이다.

 

// Filename: TestResizeOfArray01.cpp
//
// Compile: g++  -o TestResizeOfArray01 TestResizeOfArray01.cpp
// Execute: ./TestResizeOfArray01
//     Or
// Compile: cl TestResizeOfArray01.cpp /EHsc /utf-8
// Execute: TestResizeOfArray01
// Output:
//     Original size. m = 5
//     (sizeof(data) / sizeof(int)) = 2
//     [ 1  2  3  0  0 ]
//
//     Changed size. m = 10
//     (sizeof(data) / sizeof(*data)) = 2
//     [ 1  2  3  0  0  0  0  0  0  0 ]
//
//
//     Original size. m = 3, n = 4
//     (sizeof(data) / sizeof(int)) = 2
//     [[    1    2    3    1  ]
 //     [    2    3    4    2  ]
 //     [    1    0    0    0  ]]
//
//     Changed size. m = 6, n = 8
//     (sizeof(data) / sizeof(*data)) = 2
//     [[    1    2    3    1    0    0    0    0  ]
//      [    2    3    4    2    0    0    0    0  ]
//      [    1    0    0    0    0    0    0    0  ]
//      [    0    0    0    0    0    0    0    0  ]
//      [    0    0    0    0    0    0    0    0  ]
//      [    0    0    0    0    0    0    0    0  ]]
//
// Data: 2021.02.23 ~ 2021.02.24
//
// Copyright 2021 (C) pkim (_AT_) scripts (_DOT_) pe (_DOT_) kr

#include <stdlib.h>
	
#include <iostream>
#include <iterator>   // for std::size
#include <algorithm> // for copy
#include <array>
#include <iomanip>

void resize(int*& a, size_t& n)
{
   size_t new_n = 2 * n;
   int* new_a = new int[new_n];

   for (size_t i = 0; i < new_n; i++)
   {
       new_a[i] = (int) 0;
   }

   std::copy(a, a + n, new_a);
   delete[] a;
   a = new_a;
   n = new_n;
}

void print1DArray(int *data, size_t n)
{
    std::cout << "[";
    for (size_t i = 0; i < n; i++)
    {
        std::cout << " " << data[i] << " " ;
    }
    std::cout << "]" <<std::endl;
}

void test1DArray()
{
    size_t m = 5;
    int *data = new int[5] { 1, 2, 3 };

    std::cout << "Original size. m = " << m << std::endl;
    std::cout << "(sizeof(data) / sizeof(int)) = " << (sizeof(data) / sizeof(int)) << std::endl;
    print1DArray(data, m);
    std::cout << std::endl;

    resize((int *&)data, m);

    std::cout << "Changed size. m = " << m << std::endl;
    std::cout << "(sizeof(data) / sizeof(*data)) = " << (sizeof(data) / sizeof(*data)) << std::endl;
    print1DArray(data, m);
    std::cout << std::endl;
    std::cout << std::endl;
}

 resize2D(int*& a, size_t& m, size_t& n)
{
   size_t new_m= 2 * m, new_n = 2 * n, i = 0;
   int* new_a = new int[new_m * new_n];

   for (i = 0; i < new_n*new_m; i++)
   {
       new_a[i] = (int) 0;
   }

   for (i = 0; i < m; i++)
   {
       std::copy(a + i*n, a + i*n + n, new_a + i*new_n);    // copy(a[i], a[i] + 100, new_a[i]);
   }

   delete[] a;
   a = new_a;
   m = new_m;
   n = new_n;
}

void print2DArray(int *data, size_t m, size_t n)
{
    std::cout << "[[ " ;
    if ( m == 0)
    {
      	   std::cout << "]]" << std::endl;
      	   return;
    }

	if (m > 0)
	{
           for (size_t j = 0; j < n; j++)
           {
               std::cout<< " " << std::setw(3) << data[j] << " " ;
           }
           if (m > 1)
           {
               std::cout << " ]"  << std::endl;
           }
           else
           {
               std::cout << " ]]"  << std::endl;
           }

           if (m > 1)
           {
                for (size_t i = 1; i < m; i++)
                {
                	std::cout << " [ " ;
                    for (size_t j = 0; j < n; j++)
                    {
                        std::cout << " "<< std::setw(3) << data[i*n + j] << " " ;
                    }

     	              if (i < m - 1)
    	              {
                         std::cout << " ]"  << std::endl;
                    }
    	              else
    	              {
                         std::cout << " ]]"  << std::endl;
                     }
                }
           }
    }
}

void test2DArray()
{
    size_t m = 3;
    size_t n = 4;
    int *data = new int[m*n]  {  1, 2, 3 , 1, 2, 3, 4 , 2, 1 };    // { { 1, 2, 3 }, { 1, 2, 3, 4 }, {2, 1} };

    std::cout << "Original size. m = " << m << ", n = " << n << std::endl;
    std::cout << "(sizeof(data) / sizeof(int)) = " << (sizeof(data) / sizeof(int)) << std::endl;
    print2DArray((int *)&data[0], m, n);
    std::cout << std::endl;

    resize2D((int *&)data, m, n);

    std::cout << "Changed size. m = " << m << ", n = " << n << std::endl;
    std::cout << "(sizeof(data) / sizeof(*data)) = " << (sizeof(data) / sizeof(*data)) << std::endl;
    print2DArray((int *)&data[0], m, n);
    std::cout << std::endl;
}

int main()
{
    test1DArray();
    test2DArray();
    return 0;
}
Posted by Scripter
,

Visual Studio 2019 와 MSYS2 MinGW64 에서 테스트 된 소스입니다.

 혹시 MinGW 에서 컴파일되지 않으면

$ packman -S mpfr

명령으로 mpfr 라이브러리를 설치하고 컴파일하면 된다.

 

// Filename: calcGammaFn.c
//
// Compile: gcc -o calcGammaFn calcGammaFn.c -lmpfr -lgmp
// Execute: ./calcGammaFn
//     Or
// Compile: cl calcGammaFn.c /I. mpfr.lib
// Execute: calcGammaFn
//
// Date: 2021.01.28


#include <stdio.h>
#include <math.h>    // for log(10)
#include <mpfr.h>

int main()
{
  mpfr_t x; 
  int i, inex;

  mpfr_set_emin (-41);
  mpfr_init2 (x, 42);

  for (i = 1; i <= 17; i++)
  {
      mpfr_set_ui (x, i, MPFR_RNDN);
      inex = mpfr_gamma (x, x, MPFR_RNDZ);      
      mpfr_subnormalize (x, inex, MPFR_RNDZ);
      mpfr_dump (x);
  }

  printf("\n");

  for (i = 1; i <= 17; i++)
  {
      mpfr_set_ui (x, i, MPFR_RNDN);
      inex = mpfr_gamma (x, x, MPFR_RNDZ);
      mpfr_printf("Gamma(%2d) = %2d! = %Rf\n", i, i - 1, x);
  }

  printf("\n");

  for (i = 1; i <= 17; i++)
  {
      mpfr_set_ui (x, i, MPFR_RNDN);
      inex = mpfr_lngamma (x, x, MPFR_RNDZ);
      mpfr_printf("LogGamma(%2d) = Log(%2d!) = %Rf\n", i, i - 1, x);
  }

  printf("\n");

 double t10 = log(10.0);
 printf("log(10) = %f\n", t10);
 
  printf("\n");

  for (i = 1; i <= 17; i++)
  {
      mpfr_set_ui (x, i, MPFR_RNDN);
      inex = mpfr_lngamma (x, x, MPFR_RNDZ);      
      inex = mpfr_div_d(x, x, t10,  MPFR_RNDZ);
      mpfr_printf("Log10Gamma(%2d) = Log10(%2d!) = %Rf\n", i, i - 1, x);
  }

  mpfr_clear (x);
  
  return 0;
}

/*
Output:
LogGamma( 1) = Log( 0!) = 0
LogGamma( 2) = Log( 1!) = 0
LogGamma( 3) = Log( 2!) = 0.693147
LogGamma( 4) = Log( 3!) = 1.791759
LogGamma( 5) = Log( 4!) = 3.178054
LogGamma( 6) = Log( 5!) = 4.787492
LogGamma( 7) = Log( 6!) = 6.579251
LogGamma( 8) = Log( 7!) = 8.525161
LogGamma( 9) = Log( 8!) = 10.604603
LogGamma(10) = Log( 9!) = 12.801827
LogGamma(11) = Log(10!) = 15.104413
LogGamma(12) = Log(11!) = 17.502308
LogGamma(13) = Log(12!) = 19.987214
LogGamma(14) = Log(13!) = 22.552164
LogGamma(15) = Log(14!) = 25.191221
LogGamma(16) = Log(15!) = 27.899271
LogGamma(17) = Log(16!) = 30.671860

log(10) = 2.302585

Log10Gamma( 1) = Log10( 0!) = 0
Log10Gamma( 2) = Log10( 1!) = 0
Log10Gamma( 3) = Log10( 2!) = 0.301030
Log10Gamma( 4) = Log10( 3!) = 0.778151
Log10Gamma( 5) = Log10( 4!) = 1.380211
Log10Gamma( 6) = Log10( 5!) = 2.079181
Log10Gamma( 7) = Log10( 6!) = 2.857332
Log10Gamma( 8) = Log10( 7!) = 3.702431
Log10Gamma( 9) = Log10( 8!) = 4.605521
Log10Gamma(10) = Log10( 9!) = 5.559763
Log10Gamma(11) = Log10(10!) = 6.559763
Log10Gamma(12) = Log10(11!) = 7.601156
Log10Gamma(13) = Log10(12!) = 8.680337
Log10Gamma(14) = Log10(13!) = 9.794280
Log10Gamma(15) = Log10(14!) = 10.940408
Log10Gamma(16) = Log10(15!) = 12.116500
Log10Gamma(17) = Log10(16!) = 13.320620
*/

 

 

 

 

 

Posted by Scripter
,

C 언어 또는 C++ 언어에서는 문자열 인코딩을 처리할려면 무척 애을 먹는다.

반면에 Java 언어나 Python 언어에서는 문자열 인코딩 문제가

일치 감치 해결되어 있으므로 조금만 주의하면 별 어려움이 없다.

 

우선 간단한 Python 소스를 보자.

 

# -- coding: utf=8 -*-

greetings = [
        "Hello~",         # English
        "안녕하세요?",  # Korean
        "んにちは。",   # Japanese
        "您好!"          # Chinesse
    ]
for msg in greetings:
    print(msg)
    
"""
Output:
Hello~
안녕하세요?
んにちは。
您好!
"""

 

위의 소스를 저장할 때 utf8 인코딩으로 저장하면

실행 시에 터미널 환경의 문자셋 여부에 상관없이 정상적으로 잘 출력된다,

 

그러너  C 언어나 ㅊ++ 언어의 경우레는 문자열을 처리하는 방식이 너무나 다양하여

환경에 따라 정확한 처리 방식을 찾는데 꽤 고생하게 된다.

여기에 그런 수고를 덜기 위한 (중국어 간체 때문에 다소(?) 불완전한) 팁을 소개한다.

아래의 소스는 윈도우 10 환경에서

Visual Studio 2019 의 명령줄(command line) 컴파일러 cl 및

MinGW64 의 컴파알러 g++ 로 테스트된 C++ 소스이다

 

// Filename: helloUTF8_002.cpp
//
//       This source should besaved as utf-8 file=encoding.
//
// Compile: g++ -o helloUTF8_002 helloUTF8_002.cpp
// Execute: ./helloUTF8_002
//    Or
// Compile: cl helloUTF8_002.cpp /EHsc /utf-8 /std:c++17
// Execute: helloUTF8_002.cpp
// Output:
//         Hello~
//         안녕하세요?
//         んにちは。
//         ?好!
//
// Date: 2021.01.23

#include <iostream>
#include <cstddef>
#include <locale>
#include <stdio.h>
#include <wchar.h>

int main(void)
{
    const std::wstring greetings[] = {
			L"Hello~",         // English
			L"안녕하세요?",  // Korean
			L"んにちは。",   // Japanese
			L"您好!"          // Chinesse
        };

    setlocale(LC_ALL,"");   // <--- works both on cmd of Windows 10 and mys64 terminal of Windows 10

    /////////////////////////////////////////////////
    // Get the counting of an array of wstrins
    // int size = (int) size(greetings);  // <--- works on vc++, but nor on g++
    int size = *(&greetings+1)-greetings;  
            
    for (int i = 0; i <  size; ++i)
    {
          wprintf(L"%lS\n", (wchar_t *)&(greetings[i][0]));
          /// wprintf(L"%ls\n", (wchar_t *)&(greetings[i][0]));
		 	
          // std::wcout << greetings[i] << std::endl; 
    }
	
    return 0;
}

 

 

중국어 간체 您가 정상적으로 출력되지 않는 문제점이 아직 해결되지 않았다.

문자 您를 你로 바꾸어도 마찬가지이다.

Python 소스로는 잘 출력되는 것을 보면 터미널의 폰트 문제는 아난 것 같다.

 

 

Posted by Scripter
,

일반적으로 C 언어로 작성된 함수를 C++ 언어에서 불러 사용하려면

extern "C" 라는 키워드가 필요하다.

C 언어의 함수 정의를 ****.h 라는 헤더 파일에 기록해 두고

이 헤더 파일응 C++ 소스에서 포함(include)하려면 몇 가지 주의할 점이 있다.

 

우선 함수의 정의가 있는 C  소스와 C 언어용 헤더 파일을 보자.

[(구현) 파일명: sayHello.c] ------------------------------------

#include <stdio.h>

void print_message(char *message) {
    printf("%s\n", message);
}

 

위는 print_message(char *) 라는 함수 하나만 달랑 구현되어 있는 C 소스 파일이다.

 

 

[(정의) 파일명: sayHello.h] ------------------------------------

#ifndef HELLO_H_INCLUDED
#define HELLO_H_INCLUDED

#ifdef __cplusplus
extern "C" {
#endif

void print_message(char *message);

#ifdef __cplusplus
}  /* end of the 'extern "C"' block */
#endif

#endif // HELLO_H_INCLUDED

 

 

위는 함수 print_message(char *) 를 다른 C 또는 C++ 소스파일에서 불러 쓸 수 있도록

그 함수의 정의를 기헉힌 헤더 파일이다. exterb "C" { ....... } 라고 힐 경우

......... 부분이 C 언어 양식이고 이를 C++ 에서 인식할 수 있도록 하는 키워드가 extern "C" 이다.

그러나 헤더 파일에 이렇게만 적어 두면 C++ 소스에서는 불러 올 순,ㄴ 있지만, C 소스에서는

불러오지 못하는 문제사 생긴다. 이를 해경하기 위해 (즉, C 소스나 C++ 소스에서 이를 불러 쓰기 위해

추가한 것이 그 위와 아래에 적어 준 

#ifdef __cplusplus
extern "C" {
#endif

...............................................

#ifdef __cplusplus
}  /* end of the 'extern "C"' block */
#endif

부분이다.

그리고 이 헤더 파일의 시작과 끝 부분에 적어 준

#ifndef HELLO_H_INCLUDED
#define HELLO_H_INCLUDED

............................................

#endif // HELLO_H_INCLUDED

부분은 이 헤더 파일이 다른 소스에 포함(include)될 경우 단 한번 만 포함되게 하는 구문이다.

 

 

 

[파일명: hello.hpp] -------------------------------------

#ifndef HELLO_HPP_INCLUDED
#define HELLO_HPP_INCLUDED

#include "sayHello.h"

#endif // HELLO_HPP_INCLUDED

 

 

위는 C++ 소소에서 포함할 헤더 파일이다.

 

 

[파일명: callSayHello.cpp] -----------------------------------

// Filename: callSayHello.cpp
//
// Compile & Link: g++ -o callSayHello callSayHello.cpp sayHello.o
// Execute: ./callSayHello
// Output:
//     Hello world!
//     Hello world!
//
//   Or
//
// Compile: cl /c sayHello.c
// Compile & Link: cl callSayHello.cpp sayHello.c /EHsc
// Execute: .\callSayHello

// Date: 2021.01.07
//

#include <iostream>
#include "hello.hpp"

using namespace std;

int main() 
{
    cout << "Hello world!" << endl;

    // print_message ("Hello world!");
    print_message ((char *)"Hello world!");
    
    return 0; 
}

 

위는 C 언어로 작성된 함수 print_message(char *) 를 불러 사용하는 C++ 소스의 내용이다.

참고로 27째 줄 // print_message ("Hello world!"); 의 주석문 표시 // 를 제거하면,

아래와 같은 컴파일 경고(warning)가 뜬다. 이 경고가 뜨더라도 컴파일되고 실행도 되지만,

그 아래 줄 처럼 (char *) 라는 캐스팅을 추가하면 경고 없이 컴파일도 잘 되고 실행도 잘 된다.

 

$ g++ -o callSayHello callSayHello.cpp sayHello.o
callSayHello.cpp: In function 'int main()':
callSayHello.cpp:27:20: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
   27 |     print_message ("Hello world!");

 

Posted by Scripter
,