Java에서 Excel의 부동 소수점 일치
시트 1의 왼쪽 상단 셀에 숫자가 하나 있는 .xlsx 스프레드시트가 있습니다.
Excel UI에 다음이 표시됩니다.
-130.98999999999
이것은 공식 막대에서 볼 수 있습니다. 즉, 포함된 셀이 표시하도록 설정된 소수 자릿수에 영향을 받지 않습니다.이 셀에 대해 Excel이 표시하는 가장 정확한 숫자입니다.
기본 XML에는 다음이 있습니다.
<v>-130.98999999999069</v>
Apache POI를 사용하여 워크북을 읽으려고 하면 XML에서 Double.valueOf를 통해 번호를 입력하고 다음과 같은 정보를 제공합니다.
-130.9899999999907
안타깝게도 이 번호는 사용자가 Excel에서 볼 수 있는 번호와 다릅니다.사용자가 엑셀에서 보는 것과 같은 숫자를 얻을 수 있는 알고리즘을 알려줄 수 있는 사람이 있습니까?
지금까지 제가 조사한 바에 따르면 Excel 2007 파일 형식은 값 공간이 다른 약간 비표준 버전의 IEE754 부동 소수점을 사용합니다.저는 엑셀의 부동소수점을 믿으며, 이 숫자는 반올림 경계의 반대쪽에 있기 때문에 위로가 아니라 반올림된 것처럼 나옵니다.
저는 jmcnamara의 사전 답변에 동의합니다.이 대답은 거기에 확장됩니다.
각 IEEE 754 64비트 이진 부동 소수점 번호에 대해 입력 시 반올림할 수 있는 소수점 범위가 있습니다.-130.98999999999069부터 시작하는 가장 가까운 대표적인 값은 -130.9899999990686742538452144375입니다.반 짝수 규칙이 있는 반올림에서 가장 가까운 반올림 규칙에 따라, 범위가 [-130.989999999999999970098585604168447448224853515625, -130.989999999967256339913001283257146484375]인 모든 값은 해당 값으로 반올림됩니다.(중앙 번호의 이진수 표현이 짝수이므로 범위가 닫힙니다.홀수일 경우 범위가 열려 있습니다.-130.98999999999069 및 -130.9899999999907이 모두 범위 내에 있습니다.
Excel과 부동 소수점 번호가 같습니다.Excel에 입력된 것과 동일한 부동 소수점 번호를 가지고 있습니다.불행히도 추가 실험에 따르면 Excel 2007은 입력 내용 중 가장 중요한 15자리 숫자만 변환합니다.-130.98999999999069를 엑셀에 붙여넣었습니다.-130.989999999999로 표시되었을 뿐만 아니라, 이를 사용한 산술은 원래 입력이 아닌 해당 값에 가장 가까운 두 배인 -130.989999999900046532275155665313720703125와 일치했습니다.
Excel과 동일한 효과를 얻으려면 예를 들어 사용해야 합니다.BigDecimal - 십진수 15자리로 잘라낸 다음 두 배로 변환합니다.
부동 소수점 값에 대한 Java의 기본 문자열 변환은 기본적으로 원래 값으로 다시 변환할 소수점이 가장 적은 소수점 부분을 선택합니다. -130.9899999999907은 -130.989999999069보다 소수점이 더 적습니다.Excel은 숫자를 적게 표시하지만 Apache POI는 Java와 동일한 숫자로 표시됩니다.
여기 제가 이 답변에서 숫자를 얻기 위해 사용한 프로그램이 있습니다.BigDecimal을 사용하는 것은 복식의 정확한 출력을 얻고 연속된 두 복식 사이의 중간점을 계산하기 위해서입니다.
import java.math.BigDecimal;
class Test {
public static void main(String[] args) {
double d = -130.98999999999069;
BigDecimal dDec = new BigDecimal(d);
System.out.println("Printed as double: "+d);
BigDecimal down = new BigDecimal(Math.nextAfter(d, Double.NEGATIVE_INFINITY));
System.out.println("Next down: " + down);
System.out.println("Half down: " + down.add(dDec).divide(BigDecimal.valueOf(2)));
System.out.println("Original: " + dDec);
BigDecimal up = new BigDecimal(Math.nextAfter(d, Double.POSITIVE_INFINITY));
System.out.println("Half up: " + up.add(dDec).divide(BigDecimal.valueOf(2)));
System.out.println("Next up: " + up);
System.out.println("Original in hex: "+Long.toHexString(Double.doubleToLongBits(d)));
}
}
다음은 출력입니다.
Printed as double: -130.9899999999907
Next down: -130.989999999990715195963275618851184844970703125
Half down: -130.9899999999907009851085604168474674224853515625
Original: -130.98999999999068677425384521484375
Half up: -130.9899999999906725633991300128400325775146484375
Next up: -130.989999999990658352544414810836315155029296875
Original in hex: c0605fae147ae000
안타깝게도 이 번호는 사용자가 Excel에서 볼 수 있는 번호와 다릅니다.사용자가 엑셀에서 보는 것과 같은 숫자를 얻을 수 있는 알고리즘을 알려줄 수 있는 사람이 있습니까?
나는 그것이 여기서 알고리즘을 사용하고 있다고 생각하지 않습니다.은 내부적으로 를 이중으로 사용하고 Excel을 하고 있는으로 추측됩니다.printf
숫자를 표시할 때 스타일 형식:
$ python -c 'print "%.14g" % -130.98999999999069'
-130.98999999999
$ python -c 'print "%.14g" % -130.9899999999907'
-130.98999999999
은 야합다니해를 사용해야 .BigDecimal
이를 위해(정밀도를 잃지 않기 위해).
를 들어,을 ": " " "로 읽습니다.String
에 런를다음구니다성합그를 구성합니다.BigDecimal
그것으로부터.
다음은 정밀도를 잃지 않는 예제입니다.
사용자가 Excel에서 보는 것과 정확히 동일한 번호를 얻는 방법입니다.
import java.math.BigDecimal;
public class Test020 {
public static void main(String[] args) {
BigDecimal d1 = new BigDecimal("-130.98999999999069");
System.out.println(d1.toString());
BigDecimal d2 = new BigDecimal("10.0");
System.out.println(d1.add(d2).toString());
System.out.println(d1.multiply(d2).toString());
}
}
피터가 제안한 대로.페트로프 나는 이것을 위해 빅 십진법을 사용할 것입니다.언급했듯이 데이터를 손실 없이 가져오고 항상 스케일을 15로 설정하면 Excel에서와 동일한 동작을 수행할 수 있습니다.
동일한 15자리 표시 값을 계산할 때 사용합니다.
private static final int EXCEL_MAX_DIGITS = 15;
/**
* Fix floating-point rounding errors.
*
* https://en.wikipedia.org/wiki/Numeric_precision_in_Microsoft_Excel
* https://support.microsoft.com/en-us/kb/214118
* https://support.microsoft.com/en-us/kb/269370
*/
private static double fixFloatingPointPrecision(double value) {
BigDecimal original = new BigDecimal(value);
BigDecimal fixed = new BigDecimal(original.unscaledValue(), original.precision())
.setScale(EXCEL_MAX_DIGITS, RoundingMode.HALF_UP);
int newScale = original.scale() - original.precision() + EXCEL_MAX_DIGITS;
return new BigDecimal(fixed.unscaledValue(), newScale).doubleValue();
}
이 함수는 공식 막대에 표시된 것과 동일한 것을 생성해야 합니다.
private static BigDecimal stringedDouble(Cell cell) {
BigDecimal result = new BigDecimal(String.valueOf(cell.getNumericCellValue())).stripTrailingZeros();
result = result.scale() < 0 ? result.setScale(0) : result;
return result;
}
언급URL : https://stackoverflow.com/questions/28537614/matching-excels-floating-point-in-java
'source' 카테고리의 다른 글
MATLAB 코드를 Python으로 변환하는 도구 (0) | 2023.06.29 |
---|---|
python XlsxWriter 여러 셀 주위에 테두리 설정 (0) | 2023.06.29 |
Oracle에서 동일한 반복 기능 (0) | 2023.06.24 |
Java 주석을 사용하여 Spring과 함께 전자 메일 보내기 (0) | 2023.06.24 |
Git에서 두 분기를 함께 병합 (0) | 2023.06.24 |