Dav*_*rth 13 java floating-point excel openxml apache-poi
我有一个.xlsx电子表格,在表格1的左上角单元格中有一个数字.
Excel UI显示:
-130.98999999999
Run Code Online (Sandbox Code Playgroud)
这在公式栏中可见,即不受包含单元格设置为显示的小数位数的影响.这是Excel将为此单元格显示的最准确数字.
在底层XML中,我们有:
<v>-130.98999999999069</v>
Run Code Online (Sandbox Code Playgroud)
当尝试使用Apache POI读取工作簿时,它通过Double.valueOf从XML提供数字,并提供:
-130.9899999999907
Run Code Online (Sandbox Code Playgroud)
不幸的是,这与用户在Excel中看不到的数字相同.任何人都可以指向一个算法来获取用户在Excel中看到的相同数字吗?
到目前为止,我的研究表明,Excel 2007文件格式使用了IEE754浮点的略微非标准版本,其中值空间不同.我相信Excel的浮点,这个数字落在边界的另一边进行舍入,因此就像向下舍入而不是向上.
我同意jmcnamara之前的回答.这个答案扩展了它.
对于每个IEEE 754 64位二进制浮点数,有一系列小数分数将在输入时舍入到它.从-130.98999999999069开始,最接近的可表示的值是-130.98999999999068677425384521484375.在舍入到最接近圆半平均规则的范围内,[-130.9899999999907009851085604168474674224853515625,-130.9899999999906725633991300128400325775146484375]范围内的任何内容都会舍入到该值.(范围是关闭的,因为中心数字的二进制表示是偶数.如果它是奇数,则范围将是打开的).-130.98999999999069和-130.9899999999907均在范围内.
您具有与Excel相同的浮点数.
您具有与Excel输入相同的浮点数.遗憾的是,进一步的实验表明Excel 2007仅转换输入的最重要的15位数字.我将-130.98999999999069粘贴到Excel单元格中.它不仅显示为-130.98999999999,使用它的算术与该值的最接近的一倍,-130.989999999990004653227515518665313720703125,而不是原始输入.
要获得与Excel相同的效果,您可能需要使用例如BigDecimal截断为15位十进制数字,然后转换为double.
Java的浮点值的默认字符串转换基本上选择了最小的小数位数,它将转换回原始值.-130.9899999999907的小数位数少于-130.98999999999069.显然,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)));
}
}
Run Code Online (Sandbox Code Playgroud)
这是它的输出:
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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3204 次 |
| 最近记录: |