应该将 xs:double 转换为 xs:decimal 实现为 BigDecimal.valueOf(double) 还是 new BigDecimal(double)?
Posted
技术标签:
【中文标题】应该将 xs:double 转换为 xs:decimal 实现为 BigDecimal.valueOf(double) 还是 new BigDecimal(double)?【英文标题】:Should cast of xs:double to xs:decimal be implemented as BigDecimal.valueOf(double) or new BigDecimal(double)? 【发布时间】:2015-03-31 14:56:42 【问题描述】:XQuery 与 XSLT 和 XPath 2.0 及更高版本共享,支持各种数字数据类型,其中两个是 xs:double
和 xs:decimal
。可以将xs:double
转换为xs:decimal
,如http://www.w3.org/TR/xquery-operators/#casting-to-numerics 中所定义。
在 Java 中完成的实现似乎使用 Java 的 double
数据类型实现 xs:double
,使用 java.math.BigDecimal
类实现 xs:decimal
。该类支持两种将double
转换为BigDecimal
的方法,即BigDecimal.valueOf(doubleValue)
和new BigDecimal(doubleValue)
。根据https://***.com/a/7186298/252228,前者给出更直观的结果,而后者给出更正确的结果,例如BigDecimal.valueOf(1.1)
导致1.1
,而new BigDecimal(1.1)
导致1.100000000000000088817841970012523233890533447265625
。
当我尝试使用 Saxon 和 Exist 将 xs:double
转换为 xs:decimal
时
xquery version "1.0";
let $d1 as xs:double := 1.1E0
return xs:decimal($d1)
输出1.100000000000000088817841970012523233890533447265625
,而使用BaseX它输出1.1
。我想差异是由不同的实现造成的,BaseX 做BigDecimal.valueOf(1.1)
,Saxon 和 Exist 做new BigDecimal(1.1)
。
我的问题是:根据http://www.w3.org/TR/xquery-operators/#casting-to-numerics,哪种方法是实现强制转换操作的正确方法?
【问题讨论】:
我想说任何一种方式都是正确的,而且所有的实现都正确地实现了规范。它说有If ST is xs:float or xs:double, then TV is the xs:decimal value, within the set of xs:decimal values that the implementation is capable of representing, that is numerically closest to SV
,我会说两者都返回处理器能够处理的数字上最接近的值。
【参考方案1】:
xs:decimal 的实现需要支持至少 18 位有效十进制数字,如果这样做,最接近 xs:double 值 1.1 的 xs:decimal 将类似于 1.100 000 000 000 000 088 而不是1.1。 (实际上是 19 位而不是 18 位,但即使是 18 位,也应该显示与 1.1 的差异。)所以我相信返回十进制 1.1 是不符合标准的。位数由实现定义超过 18 位,但前 18 位必须基本上如图所示。
Saxon 中的实际代码是
public DecimalValue(double in) throws ValidationException
try
BigDecimal d = new BigDecimal(in);
value = d.stripTrailingZeros();
catch (NumberFormatException err)
//...
【讨论】:
我不太明白。如果规范说“至少 18 位有效的十进制数字”,为什么返回1.100 000 000 000 000 000
(可以缩短为 1.1
而不会丢失有效数字)是不正确的?
我不会这么说的。 1.1 和 1.100 000 000 000 000 000 只是相同数字的不同表示,所以如果你实际上没有返回任何不同的东西。我说的是double 1.1e0的数值和decimal 1.1的数值不一样,如果你把小数保持到18位或更多,那么差异就会显现出来。
@MichaelKay,如果 XQuery 3.0 实现至少需要 18 个有效数字来实现 xs:decimal
,format-number(xs:decimal(1.1E0), '0.00000000000000000') eq '1.10000000000000009'
是否会成为测试用例?
@MartinHonnen,是的,我认为这将是一个合法的测试,尽管我对将边缘案例放入测试套件的兴趣因它们倾向于引起的讨论量而减弱......【参考方案2】:
XQuery 建议将双精度值的最接近十进制表示应该是什么由实现决定。原因是规范应该支持任意编程语言的实现。
但是,由于您提示 BigDecimal.valueOf(d)
和 new BigDecimal(d)
不等价,BaseX 的下一个版本(8.0 版)将返回更准确的结果。
【讨论】:
以上是关于应该将 xs:double 转换为 xs:decimal 实现为 BigDecimal.valueOf(double) 还是 new BigDecimal(double)?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我应该“将 myVariable 转换为最终的一个元素数组”?
为啥我应该“将 myVariable 转换为最终的一个元素数组”?
将屏幕截图捕获为图像然后使用 ffmpeg 将图像转换为视频文件的时间应该是啥?