우리는 모두 학교에서 부동 소수점 수에 대해 배웠습니다. 소수점이 없는 정수와 반대입니다.
숫자 5가 정수이고 5.43이 부동 소수점 숫자라는 것을 알기 위해 수학 전문가가 될 필요는 없습니다. 분명히 우리 모두는 둘 사이의 명확한 구별점인 소수점에 대해 잘 알고 있습니다.
물론, 부동 소수점이 프로그래밍 언어에 사용되며 일상적인 계산에 얼마나 필요한지 때문에 별도의 원시 데이터 유형으로 분류된다는 것은 놀라운 일이 아닙니다.
Python이나 Java를 배우기 시작하면 float 데이터 유형을 사용하게 됩니다. 특히 두 개의 부동 소수점 숫자 자체 사이에서 나누기 연산을 수행하는 경우.
Python에서는 부동 소수점 숫자를 선언할 필요가 없더라도 이 숫자 중 두 개를 나누면 아래와 같이 동일한 데이터 유형의 숫자가 나올 수 있습니다.
사용된 모든 변수에 대해 'float' 기본 데이터 유형을 명시적으로 선언해야 하는 경우에도 Java에서도 동일한 결과를 생성할 수 있습니다.
알 수 있듯이 이러한 종류의 계산은 여러 응용 프로그램에 필요하며 이것이 바로 부동 소수점 숫자를 사용한 연산이 두 프로그래밍 언어의 기능으로 사용 가능한 이유입니다.
전혀 다르게, Solidity를 배워본 적이 있다면 여기에 부동 소수점 숫자 데이터 유형이 포함되어 있지 않다는 점을 알아차렸을 것입니다.
대신 Solidity에서 코드를 작성한 경우 부호 있는 정수 유형이나 부호 없는 정수 유형의 숫자를 사용하게 됩니다.
따라서 이전 섹션에 표시된 대로 동일한 계산(5를 2로 나눈 값)을 수행하려는 경우 스마트 계약의 일부로 다음과 같이 표시됩니다.
그러나 Solidity는 float 데이터 유형을 지원하지 않기 때문에 얻는 결과는 동일하지 않습니다. 적어도 아직은 아닙니다.
특히 Solidity는 결과를 0으로 반올림합니다. 이 경우 위에 표시된 대로 값은 2가 됩니다. 이것을 두 숫자에 대한 모듈로 연산으로 생각하십시오.
일반적인 상황에서는 별 문제가 되지 않지만 결과가 계산 오류로 이어질 수 있는 경우가 있습니다. 그렇기 때문에 분할작업은 최대한 피하거나 연기하는 것이 좋습니다.
학교의 BODMAS 규칙에 따라 모든 수학 학생이 곱셈 전에 나눗셈 계산을 수행해야 한다고 하더라도 Solidity에서 정수 나눗셈을 수행해야 하는 경우에는 권장되지 않습니다.
이 간단한 예를 통해 세 개의 숫자로 곱셈과 나눗셈을 수행하는 이유를 알아 보겠습니다.
스마트 컨트랙트 배포 시 숫자 1, 3, 5를 입력하면 getResult, getResult2 함수에 동일한 값을 얻어야겠죠?
간단한 계산기를 사용하면 1.666이라는 부동 소수점 값을 얻어야 합니다. 이는 부동 소수점 값이 없기 때문에 Solidity에서는 1로 변환됩니다.
안타깝게도 아래와 같이 getResult 및 getResult2 함수의 결과를 확인할 때 이런 일이 발생하지 않습니다.
나눗셈을 먼저 수행하면 최종 결과는 0이 됩니다. 기대값 1과 반대로 getResult 함수에서 해당 작업을 끝까지 연기하는 경우입니다.
알 수 있듯이 부동 소수점 값이 없을 것으로 예상하여 이 값을 계산했더라도 정밀도가 떨어질 수 있는 오류가 여전히 발생합니다. 이는 쉽게 우회할 수 있는 경제적 손실로 이어질 수 있습니다.
그렇다면 정수 나누기 오류를 어떻게 방지할 수 있을까요? 더 중요한 것은 어떻게 계산의 정밀도를 높일 수 있을까요? 이를 수행하는 가장 일반적인 세 가지 방법을 알아 보겠습니다.
이 오류를 우회하는 방법에는 여러 가지가 있으므로 가장 간단한 수정부터 시작하여 하루를 마무리하기 전에 몇 가지를 더 살펴보겠습니다.
방법 #1: 승수 사용
이제 나누기 연산을 마지막에 배치하는 것과 함께 오류나 부정확한 값이 발생하지 않도록 하는 한 가지 방법은 승수를 사용하는 것입니다. 아래 예에서는 이전에 사용한 것과 동일한 세 개의 숫자와 함께 100의 승수를 사용합니다.
이제 다음 코드를 사용하여 계약을 배포하고 두 함수를 모두 호출하면 다음과 같은 결과가 출력됩니다.
원하는 출력은 1.666 또는 166/100이므로 승수가 세 숫자와 함께 작동할 때 getResult2 값이 필요한 정확도를 제공한다는 것을 알 수 있습니다. 물론, getResult 함수처럼 승수를 사용하지 않으면 1을 얻게 된다.
Solidity를 사용할 때 기본적으로 예상되는 대로 결과에서 0.666이 잘립니다. 따라서 이 값을 검색하려면 결과를 승수로 나누기만 하면 됩니다.
아시다시피 Solidity는 부호 있는 정수와 부호 없는 정수 모두의 경우 반올림 시 0을 향해 이동합니다. 따라서 이 수정 사항은 부호 있는 정수의 경우에도 작동합니다. 인수를 빼기 1로 아래 코드를 배포하고 실행하면 됩니다. , 3, 5:
부호 있는 정수에 대한 함수에 의해 생성된 값은 다음과 같습니다.
분명히 승수를 사용하여 부호 있는 정수에 대해서도 정밀도를 유지할 수 있습니다. 그러나 다음에 살펴보게 될 부호 있는 정수를 반올림하는 정확한 방법이 있습니다.
방법 #2: 부호 있는 정수에 바닥 나누기 사용
이제 아래 수직선을 보면 부호 없는 두 정수 사이의 정수 나누기를 수행한 결과가 0에 가깝게 반올림됩니다. 1.666을 구하는 경우와 마찬가지로 Solidity에서는 더 작은 숫자인 1로 반올림합니다.
그러나 부호 있는 정수의 경우 -1.6666의 결과는 두 숫자 중 더 큰 -1로 반올림됩니다. 따라서 Solidity에서 기본적으로 구현되는 반올림하여 0으로 나누는 것이 아니라 여기에 바닥 나누기를 적용해야 합니다. 물론 정확성을 위해서입니다.
float 데이터 유형을 사용할 수 있는 경우 -1.666 값이 계산됩니다. Solidity는 이를 -1로 반올림하지만, 부호 있는 정수에 바닥 나누기를 적용하면 -2로 줄어듭니다.
getResult 및 getResult2 함수를 호출하면 인수에서 1, 3, 5를 뺀 값에 대해 아래와 같은 값을 얻습니다.
알 수 있듯이 getResult는 0으로 반올림되는 접근 방식을 사용하여 값을 계산하는 반면 getResult2는 바닥 나누기 덕분에 여기에서 값을 가장 작은 정수로 반올림합니다.
방법 #3: ABDKMath64x64 라이브러리 사용
이제 마지막 방법으로 나누기 연산의 결과를 고정 소수점 숫자로 변환하는 ABDKMath64x64 라이브러리를 사용하겠습니다.
그러나 이 라이브러리를 사용하면 Solidity에서 기본적으로 사용할 수 있는 0으로 반올림하는 방법과 달리 정확도가 향상된다고 합니다. 출력을 이해하기 위해 아래와 같이 add 결과를 div 함수와 비교해 보겠습니다.
스마트 계약을 배포할 때 인수 1, 1, 1을 추가하면 아래와 같은 값을 얻게 됩니다.
add 함수가 2라는 정수 값을 반환하고 세 개의 인수를 더해 그 값을 얻는다는 것은 놀라운 일이 아닙니다. div 함수의 경우 부호 있는 64.64비트 고정 소수점 숫자를 나타내고 일반적인 숫자 연산을 수행할 수 있는 int 128 값이 반환됩니다.
물론 ABDKMath64x64 라이브러리는 정수 나누기 오류를 방지하는 것 외에 정확도를 향상시키는 데 사용할 수 있는 유일한 라이브러리는 아닙니다.
다른 숫자 형식을 사용하는 Fixidity , DSMath 및 BANKEX 라이브러리와 같은 몇 가지 예가 있습니다. 위의 예에서 사용된 64.64비트 고정 소수점 숫자 형식과 다른 숫자 형식입니다. 따라서 이러한 라이브러리는 탐색하는 데 유용할 수 있지만 해당 숫자 형식은 사용 가능한 다른 라이브러리에서는 작동하지 않는다는 점을 기억하십시오.