자바와 C++로 돈 버는 방법 : IEEE 754와 실생활 : 0.1+0.2=0.3 그래, 하지만 어떻게?
바로 본론으로 들어가 돈을 조심스럽게 다루어야 하는 이유를 알아보겠습니다.
여기서 Java는 Java 17을 나타내고 C++는 C++ 20을 나타냅니다. 다르게 지정되지 않으면 Java 및 C++ 모두에 대해 w3schools 컴파일러를 사용합니다.
문제를 이해하는 좋은 시작은 link을 따르는 것입니다.
C++ : 0.1d + 0.2d != 0.3d
#include <iostream>
#include <limits>
typedef std::numeric_limits< double > dl;
int main() {
std::cout.precision(dl::max_digits10);
std::cout<<0.1d<<std::endl;
std::cout<<0.2d<<std::endl;
std::cout<<0.3d<<std::endl;
std::cout<<0.1d + 0.2d<<std::endl;
std::cout<<(0.1d + 0.2d == 0.3d)<<std::endl;
return 0;
}

IEEE 754 다음에 오는 메모리 내 표현은 근사치라는 것이 분명합니다. 예상했던 것과 항상 동일한 근사값을 갖는다는 보장은 없습니다.
자바: 0.1d + 0.2d != 0.3d
Java에서 같은 것을 확인해 봅시다.
public class Main {
public static void main(String[] args) {
final String DOUBLE_FORMAT = "%.17f";
System.out.println(String.format(DOUBLE_FORMAT, 0.1d));
System.out.println(String.format(DOUBLE_FORMAT, 0.2d));
System.out.println(String.format(DOUBLE_FORMAT, 0.3d));
System.out.println(String.format(DOUBLE_FORMAT, 0.1d + 0.2d));
System.out.println(0.1d + 0.2d == 0.3d);
}
}

따라서 우리는 Java에서도 이중 표현을 사용할 수 없습니다.
합계 오류 감소
부동 소수점 숫자를 추가할 때 보다 일관된 결과를 얻으려면 Kahan summation algorithm을 확인하십시오.
그렇지 않으면 문제를 있는 그대로 해결하려면 더 간단한 것이 필요하며 그 이름은 decimal data type support 입니다.
사실 우리는
0.1
를 1
만큼 오른쪽으로 이동한 것으로 나타낼 수 있으며, 0.2
및 0.3
와 동일합니다. 그런 다음 0.1
및 0.2
를 추가하는 것이 간단합니다. 1
) 및 2
결과를 오른쪽으로 이동합니다. 사실 우리는 시프트가 있는 정수로 십진수 값을 나타냅니다. 자세한 내용은 decimal 64 링크를 참조하십시오.10진수 유형으로 작업할 때 할당은 부동 소수점 유형이 아니라 문자열에서 10진수로 발생합니다.
C++ : 부스트 다중 정밀도 0.1 + 0.2 == 0.3
boost multiprecision
10진수 유형을 사용하여 다음을 얻습니다
0.1 + 0.2 == 0.3
.#include <boost/multiprecision/cpp_dec_float.hpp>
#include <iostream>
using boost::multiprecision::cpp_dec_float_50;
int main() {
cpp_dec_float_50 d01("0.1");
cpp_dec_float_50 d02("0.2");
cpp_dec_float_50 d03("0.3");
std::cout<<std::fixed<<std::setprecision(50)<<d01<<std::endl;
std::cout<<std::fixed<<std::setprecision(50)<<d02<<std::endl;
std::cout<<std::fixed<<std::setprecision(50)<<d03<<std::endl;
std::cout<<std::fixed<<std::setprecision(50)<<(d01 + d02)<<std::endl;
std::cout<<std::fixed<<std::setprecision(50)<<(d01 + d02 == d03)<<std::endl;
return 0;
}

곱셈도 간단합니다.
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <iostream>
using boost::multiprecision::cpp_dec_float_50;
int main() {
cpp_dec_float_50 d01("0.1");
cpp_dec_float_50 d02("0.2");
std::cout<<std::fixed<<std::setprecision(50)<<d01 * d02<<std::endl;
return 0;
}

부서는 어떻습니까? 기본적으로 예상한 대로 작동합니다.
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <iostream>
using boost::multiprecision::cpp_dec_float_50;
int main() {
cpp_dec_float_50 d1("1");
cpp_dec_float_50 d2("2");
cpp_dec_float_50 d3("3");
std::cout<<std::fixed<<std::setprecision(50)<<d1 / d2<<std::endl;
std::cout<<std::fixed<<std::setprecision(50)<<d1 / d3<<std::endl;
std::cout<<std::fixed<<std::setprecision(50)<<d2 / d3<<std::endl;
return 0;
}

자바 : 큰 십진수 0.1 + 0.2 == 0.3
Big Decimal
BigDecimal을 사용한 요약:
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
final String DOUBLE_FORMAT = "%.17f";
BigDecimal d01 = new BigDecimal("0.1");
BigDecimal d02 = new BigDecimal("0.2");
BigDecimal d03 = new BigDecimal("0.3");
System.out.println(String.format(DOUBLE_FORMAT, d01));
System.out.println(String.format(DOUBLE_FORMAT, d02));
System.out.println(String.format(DOUBLE_FORMAT, d03));
System.out.println(String.format(DOUBLE_FORMAT, d01.add(d02)));
System.out.println(d01.add(d02).equals(d03));
}
}

곱셈 :
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
final String DOUBLE_FORMAT = "%.17f";
BigDecimal d01 = new BigDecimal("0.1");
BigDecimal d02 = new BigDecimal("0.2");
System.out.println(String.format(DOUBLE_FORMAT, d01.multiply(d02)));
}
}

예상한 대로 작동합니다. 나눗셈에는 특수성이 있습니다. 나눗셈의 결과를 유한 십진법으로 표현할 수 있어야 하거나 MathContext을 전달해야 합니다.
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
final String DOUBLE_FORMAT = "%.17f";
BigDecimal d1 = new BigDecimal("1");
BigDecimal d2 = new BigDecimal("2");
BigDecimal d3 = new BigDecimal("3");
System.out.println(String.format(DOUBLE_FORMAT, d1.divide(d2)));
}
}


import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) {
final String DOUBLE_FORMAT = "%.17f";
BigDecimal d1 = new BigDecimal("1");
BigDecimal d2 = new BigDecimal("2");
BigDecimal d3 = new BigDecimal("3");
System.out.println(d1.divide(d3, MathContext.DECIMAL32));
System.out.println(d1.divide(d3, MathContext.DECIMAL64));
System.out.println(d1.divide(d3, MathContext.DECIMAL128));
System.out.println(d1.divide(d3, 2, RoundingMode.HALF_EVEN));
}
}

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) {
final String DOUBLE_FORMAT = "%.17f";
BigDecimal d1 = new BigDecimal("1");
BigDecimal d2 = new BigDecimal("2");
BigDecimal d3 = new BigDecimal("3");
System.out.println(d2.divide(d3, MathContext.DECIMAL32));
System.out.println(d2.divide(d3, MathContext.DECIMAL64));
System.out.println(d2.divide(d3, MathContext.DECIMAL128));
System.out.println(d2.divide(d3, 2, RoundingMode.HALF_EVEN));
}
}

요약
돈으로 작업할 때마다 정수 또는 십진수 유형을 사용하는 것이 좋습니다. 필요에 따라 사용자 정의 10진수 유형을 작성해야 할 수 있습니다. 이 경우 일반적인 지침은 다음과 같습니다.
따라서 제공된 10진수 유형 또는 lib의 사용을 고려하는 것이 더 나은 아이디어일 수 있습니다.
잘 지내세요.
Reference
이 문제에 관하여(자바와 C++로 돈 버는 방법 : IEEE 754와 실생활 : 0.1+0.2=0.3 그래, 하지만 어떻게?), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/hexfloor/how-to-make-money-with-java-and-c-ieee-754-and-the-real-life-010203-yes-but-how-5244텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)