如何在Marklogic中将大型指数值转换为整数或小数格式
报错原因
MarkLogic 内置的 xs:decimal 类型默认仅支持最多18位整数部分 + 18位小数部分的数值,超出这个范围的 xs:double 类型科学计数法数值转换时就会触发 XDMP-CAST 溢出错误,这就是你使用3.22122E+23转换失败、3.22122E+18可以转换成功的原因。
处理超大高精度数值的方案
1. 自定义科学计数法解析函数
如果需要保留全精度用于展示或计算,可以自行实现科学计数法的字符串解析逻辑,拆分系数和指数后生成对应的十进制字符串,参考实现如下:
declare function local:sci-to-decimal($sci-str as xs:string) as xs:string { (: 统一转大写,拆分系数和指数部分 :) let $parts := tokenize(upper-case($sci-str), 'E') let $coeff := replace($parts[1], '\.', '') (: 计算原系数的小数点位置 :) let $dec-pos := if (contains($parts[1], '.')) then string-length(substring-before($parts[1], '.')) else string-length($parts[1]) let $exp := xs:integer($parts[2]) let $new-dec-pos := $dec-pos + $exp return (: 正指数右移小数点,补0到末尾 :) if ($new-dec-pos > string-length($coeff)) then concat($coeff, string-join((1 to ($new-dec-pos - string-length($coeff))) ! '0', '')) (: 负指数左移小数点,前面补0 :) else if ($new-dec-pos <= 0) then concat('0.', string-join((1 to (-$new-dec-pos)) ! '0', ''), $coeff) (: 小数点落在系数中间的情况 :) else concat(substring($coeff, 1, $new-dec-pos), '.', substring($coeff, $new-dec-pos + 1)) };
调用示例效果:
- 输入
local:sci-to-decimal("3.22122E+23"),返回322122000000000000000000 - 输入
local:sci-to-decimal("3.22122E-23"),返回0.0000000000000000000000322122
可以完全满足小数点后20位以上的展示需求。
2. 高精度计算适配
如果需要对这类超大数值做加减乘除运算,可以基于上面输出的字符串格式高精度数,自行实现对应的字符串运算逻辑,MarkLogic的原生字符串处理性能够覆盖绝大多数常规高精度计算场景。
3. 非全精度场景简化方案
如果业务允许一定精度损失,可以直接使用xs:double类型存储和计算这类超大数值,仅在输出展示时调用上面的解析函数转换为普通十进制格式即可。
内容的提问来源于stack exchange,提问作者ajitvg
相关产品推荐
相关产品推荐

