정수부의 자리수가 조금 큰 부동소수점수(64비트 double 포맷의 수)를 십진수 표현으로 출력해 보았습니다.

십진수로 표현하면  유효자리수 개수가 약 14~15개 정도인데,

Java 언어로는 유효수자 개수를 17개로 자르고 그 뒤를 모두 0으로 출력하였고,

C# 언어로는 유효수자 개수를 15개로 자르고 그 뒤를 모두 0으로 출력하였지만,

Python 언어로는 유효수자 아래 부분을 자르지 않고 모두 츨력합니다.

Pyhon 은 C/C++ 의 경우와 비슷합니다.

 

물론 Java, C#, Python, C, C++ 어느 프로그램 언어든

십진수로 표현할 때 자르는 방법이나 유효수자 아래 부분을 채우는 방법은 다르지만,

덧셈, 뺄셈, 곱셈, 나누셈, 기타 등등에서 유효수자 아래부분의 처리 결과는 대동소이합니다.

 

# -*- coding: utf-8 -*-
#
# Filename: Test_Of_Native_Double_Precison_01.py
#
#
#
# Execute: python Test_Of_Native_Double_Precison_01.py
# Output:
#     math.pow(2, 128) = 340282366920938463463374607431768211456.000000
#     math.pow(2, 128) = 340282366920938463463374607431768211456
#               2**128 = 340282366920938463463374607431768211456
#
#
#  ------------------------------------------------------
#  출처: https://scripting.tistory.com/
#  ------------------------------------------------------


import math

def test_01():
    y = math.pow(2, 128)
    print("math.pow(2, 128) = %f" % y)
    print("math.pow(2, 128) = %.f"% y)

    z = 2**128
    print("          2**128 = %d" % z)

    
if __name__ == "__main__":
    test_01()
    print("")

 

 

Posted by Scripter
,

정수부의 자리수가 조금 큰 부동소수점수(64비트 double 포맷의 수)를 십진수 표현으로 출력해 보았습니다.

십진수로 표현하면  유효자리수 개수가 약 14~15개 정도인데,

17개로 자르고 그 뒤는 모두 0으로 출력합니다.

C# 의 것과 비슷하지만 자르는 유효수자 개수가 다릅니다.

Pyhon 이나 C/C++ 의 경우는 자르지 않고 소수점 위 부분을 모두 출력합니다.

 

물론 Java, C#, Python, C, C++ 어느 프로그램 언어든

십진수로 표현할 때 자르는 방법이나 유효수자 아래 부분을 채우는 방법은 다르지만,

덧셈, 뺄셈, 곱셈, 나누셈, 기타 등등에서 유효수자 아래부분의 처리 결과는 대동소이합니다.

 

 

// Filename: Test_Of_Native_Double_Precison_001.java
//
//
// Compile: javac -d . Test_Of_Native_Double_Precison_001.java
// Execute: java Test_Of_Native_Double_Precison_001
// Output:
//                 Math.pow(2, 128) = 340282366920938460000000000000000000000.000000
//                 Math.pow(2, 128) = 340282366920938460000000000000000000000
//     new BigInteger("2").pow(128) = 340282366920938463463374607431768211456
//
//
//  ------------------------------------------------------
//  출처: https://scripting.tistory.com/
//  ------------------------------------------------------


import java.math.BigInteger;
import java.util.Arrays;

public class Test_Of_Native_Double_Precison_001
{
    public static void test_01() 
    {
          double y = Math.pow(2, 128);
          System.out.printf("            Math.pow(2, 128) = %f\n", y);
          System.out.printf("            Math.pow(2, 128) = %.0f\n", y);

          BigInteger z = new BigInteger("2").pow(128);
          System.out.printf("new BigInteger(\"2\").pow(128) = %s\n", z);
    }
    
    public static void main(String[] args)
    {
        test_01();
        System.out.println();
    }
}
Posted by Scripter
,

 

10**100 을 7로 나누고 다시 7을 곱해 보았습니다.

 math.floor() 함수는 유효숫자의 개수 때문에 오차가 좀(?) 많네요.

(float 인스턴스).as_integer_ratio()  메서드로도 확인해 보았습니다.

 

 

>>> import math

>>> help(math.floor)
Help on built-in function floor in module math:

floor(x, /)
    Return the floor of x as an Integral.

    This is the largest integer <= x.
>>> math.floor(1.0/7*1.0E100)*7
9999999999999999916195299569383046603428070211005418059480472676230398615693829843450434335205752832
>>> 10**100 // 7
1428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428571428
>>> 10**100 // 7 * 7
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999996
>>> (0.7*1E100).as_integer_ratio()
(6999999999999999528519570100600705052013993947706024980124877574781630764975543820898143428537221120, 1)

 

 

 

 

 

 

Posted by Scripter
,

어떤 프로그램 언어를 쓰든 0.1 + 0.2 ≠ 0.3 이 되는 것이 당연(?)합니다.

또한 1.0/(0.1 + 0.2 - 0.3)   ∞ 이 되는 것도 당연(?)합니다.

>>> 0.1 + 0.2 - 0.3
5.551115123125783e-17
>>> 0.1 + 0.2 - 0.3 > 1.0E-17
True
>>> 0.1 + 0.2 - 0.3 > 1.0/10E17
True

0.1 + 0.2 - 0.3 이 1.0E-17 즉 0.000000000000000001 보다 크다고 합니다.

 

이래 가지고서는 부동소수점수의 계산 결과를 어디 까지 믿을 수

있는지 의문입니다.

(보통으로는 배정밀도 부동소수점수는 유효수자 개수가 14개~15개 정도입니다.)

 

다행히 Python 의 fractions 모듈과 decimal 모듈을 사용하면

이런 오류를 쉽게 복구할 수 있습니다.

아래는 Python 3 인터프리터로 실행한 결과입니다.

(fractions.Fraction 객체).limit_denominator() 가 문제 해결의 핵심입니다.

(조금 번거럽네요.)

 

>>> import fractions, decimal, math
>>> a = fractions.Fraction(0.1)
>>> b = fractions.Fraction(0.2)
>>> c = fractions.Fraction(0.3)
>>> a + b == c
False
>>> 0.1 + 0.2 == 0.3
False
>>> a + b > c
True
>>> 0.1 + 0.2 > 0.3
True
>>> a + b - c
Fraction(1, 36028797018963968)
>>> 0.1 + 0.2 - 0.3
5.551115123125783e-17
>>> a2 = a.limit_denominator()
>>> b2 = b.limit_denominator()
>>> c2 = c.limit_denominator()
>>> a2, b2, c2
(Fraction(1, 10), Fraction(1, 5), Fraction(3, 10))
>>> a, b, c
(Fraction(3602879701896397, 36028797018963968), Fraction(3602879701896397, 18014398509481984), Fraction(5404319552844595, 18014398509481984))
>>> a2 + b2
Fraction(3, 10)
>>> "%s" % (a2 + b2)
'3/10'
>>> print("%s" % (a2 + b2))
3/10
>>> c2
Fraction(3, 10)
>>> "%s" % (c2)
'3/10'
>>> print("%s" % (c2))
3/10
>>> a2 + b2 == c2
True
>>> a2 + b2 - c2
Fraction(0, 1)
>>> print(a2 + b2 - c2)
0
>>> print("%s" % (a2 + b2 - c2))
0
>>> print("%d" % (a2 + b2 - c2))
0
>>> print("%d" % (a2 + b2))
0
>>> print("%d" % (c2))
0

 

 

Posted by Scripter
,

부동소수점수에 '를 붙이면 가독성이 좋아 실수를 덜 합니다.

아래의 소스는 MSVC 와 g++ 로 잘 컴파일되고 동일한 실행 결과를 얻습니다.

// Filename: test_cpp_literal.cpp
//
// Compile: cl /utf-8 /EHsc test_cpp_literal.cpp
// Execute: test_cpp_literal
//
//            Or
//
// Compile: g++ -o test_cpp_literal test_cpp_literal.cpp
// Execute:./test_cpp_literal
//
// Output:
//     pie =
//     3.14159
//     3.14159
//     3.141593
//     3.141593e+00
//
//       pie =
//       3.141593
//       3.14159
//       3.141593
//       3.141593e+00
//
//     ee =
//     2.71828
//     2.71828
//     2.718280
//     2.718280e+00
// 
//       ee =
//       2.71828
//       2.71828
//       2.718280
//       2.718280e+00
//
//
// Date: 2023.01.03
//
// Copyright (c) 2023 scrpting.tistory.com
// Liscense: BSD-3


#include <iostream>
#include <iomanip>     // for std::setprecision()
#include <cstdio>

using namespace std;

#if defined(__cplusplus)
double pie = 3.141'593;
#endif

int main()
{
    cout << "pie = " << endl;
    cout << pie << endl;
    printf("%g\n", pie);
    printf("%f\n", pie);
    printf("%e\n", pie);
    cout << endl;

    cout << "  pie = " << endl;
    cout << "  " << setprecision(10) << pie << endl;
    printf("  %lg\n", pie);
    printf("  %lf\n", pie);
    printf("  %le\n", pie);
    cout << endl;

#if defined(__cplusplus)
double ee = 2.718'28;
#endif

    cout << "ee = " << endl;
    cout << ee << endl;
    printf("%g\n", ee);
    printf("%f\n", ee);
    printf("%e\n", ee);
    cout << endl;

    cout << "  ee = " << endl;
    cout << "  " << setprecision(10) << ee << endl;
    printf("  %lg\n", ee);
    printf("  %lf\n", ee);
    printf("  %le\n", ee);
    cout << endl;

    return 0;
}

 

 

 

 

Posted by Scripter
,

먼저 파이썬 인터프리터를 실행하여 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
,

지난 2021년 12월 6일에 Python 3.10.1 이 출시되었다.

 

설치하자 마자 인터프리터 쉘로 간단히 테스트해 보았다.

 

Python 3.10.1 (tags/v3.10.1:2cd268a, Dec  6 2021, 19:10:37) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> print("Hello~")
Hello~
>>> for i in range(11):
...     print(2**i)
...
1
2
4
8
16
32
64
128
256
512
1024
>>> import math
>>> math.factorial(10)
3628800
>>> math.gamma(11)
3628800.0
>>> math.lgamma(11)
15.104412573075514
>>> math.log(math.gamma(11))
15.104412573075516
>>> math.exp(10)
22026.465794806718
>>> 10**10
10000000000
>>> 10.0**10.0
10000000000.0
>>> math.pow(10.0, 10.0)
10000000000.0
>>> math.exp(10.0*math.log(10.0))
10000000000.00004
>>> math.sqrt(10)
3.1622776601683795
>>> math.cbrt(10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'math' has no attribute 'cbrt'
>>> math.tan(1)
1.5574077246549023
>>> math.atan(1)
0.7853981633974483
>>> 4*(math.atan(1.0/5)*4 - math.atan(1.0/239))
3.1415926535897936
>>> math.pi
3.141592653589793
>>> math.e
2.718281828459045
>>> math.exp(1)
2.718281828459045
>>> (math.exp(1) - math.exp(-1))/2
1.1752011936438014             ^
SyntaxError: unmatched ')'
>>> math.sinh(1)
1.1752011936438014
>>> math.atan(1)
0.7853981633974483
>>> math.gcd(625, 1000)
125
>>> quit()

 

아직 세제곱근(cube root) 구하는 math.cbrt(float) 함수는 구현되지 않았다.

 

-----------------------------------------------------------------------------

Union 타입 생성  연산자 '|' 기 Python 3.10.x 에 추가되었다.


>>> # Union 타입을 매개변수로 방아 Union 타입을 리턴허눈 험수
>>> # def f(list: list[Union[int, str]], param: Optional[int]) -> Union[float, str]

>>>  # 대신, 다음 구문으로 가능

>>> def f(list: list[int | str], param: int | None) -> float | str:
.>>> ..     pass
...


>> int 타입과 list[int] 타입의 Union 타입

>>> str(int | list[str])
'int | list[str]'



>>> 두 int 타입의 Union 타입
>>> str(int | int)
"<class 'int'>"


>>> Union 타입을 이용헌 인스턴스 확인

>>> isinstance("", int | str)
True


>>> Union 타입을 이용헌 상속 확인

>>> issubclass(bool, int | float)
True

 

>>> # 클래스 상속과 Union 타입 생성 연산자 확인
>>> class M(type):
...         def __or__(self, other): return "Hello"
...
>>> class C(metaclass=M): pass
...

>>> C | int
'Hello'


>>> int | C
int | __main__.C

 

-------------------------------------------------

다음은 아직은 아니지만 다음 릴리즈에 추가될 것 중의 하나이다.

우선 다음 내용을 test,py 라는 파일명으로 자장하고,

x = {'a':{'b':{'c':None}}}
x['a']['b']['c']['d'] = 1

 

프롬프트> python test,py

명령으로 실행합니다. 그러면 다음 에러 메시지가 나온다.

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    x['a']['b']['c']['d'] = 1
TypeError: 'NoneType' object does not support item assignment

 

이 에러 메시지 만으로는 시퀀스 타입 자료에 어느 것이 None 인지 알 수가 없다.

다음 릴리즈에서는 에러 메시지가

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    x['a']['b']['c']['d'] = 1
    ~~~~~~~~~~~^^^^^
TypeError: 'NoneType' object is not subscriptable

로 출력된다. (이는 핸재 Python Pre-Release 3.11.0a3 에서 확인되옸다.)

 

 

 

 

 

 

Posted by Scripter
,