AviatorScript关于精度的坑(Double类型的0.0000000000000001问题)

参考链接

  1. AviatorScript 文档 - 最佳实践
  2. double java 坑,Java中四则运算的那些坑

1. 问题复现

比较运算符 两边的计算结果和静态值明明人眼观察下是相等的,为什么得到的结果却与人算的出来的结果不一致呢?

    @Test
    public void five(){
        String uiExpression = "if( 指标.完成值   /   指标.目标值   >=  0.95 ){ 8 }else{ 0 }";
        Map<String, Object> variable = new HashMap<>();
        variable.put("指标.目标值", new BigDecimal("100"));
        variable.put("指标.完成值", new BigDecimal("95"));

        // 是否跟踪运行,打开后将在控制台打印整个表达式的求值过程。请勿在生产环境打开,将极大地降低性能。默认为 false 关闭。
        AviatorEvaluator.getInstance().setOption(Options.TRACE_EVAL, true);

        // 执行表达式
        final Object execute = AviatorEvaluator.execute(uiExpression, variable);

        // 期望得到 8
        Assertions.assertEquals("8", execute.toString());
    }
[Aviator TRACE]          <JavaType, 指标.完成值, 95, java.math.BigDecimal> / <JavaType, 指标.目标值, 100, java.math.BigDecimal> => <Decimal, 0.95>
[Aviator TRACE]          <Decimal, 0.95> >= <Double, 0.9500000000000001> => <Boolean, false>
[Aviator TRACE] Func   : Lambda_1678191364121_58()
[Aviator TRACE] Tracing: null
[Aviator TRACE] Result : null
[Aviator TRACE] Result : <JavaType, A_0, 0, java.lang.Long>
[Aviator TRACE] Func   : __if_callcc(<JavaType, A_0, 0, java.lang.Long>,<Lambda, Lambda_1678191364121_59>)
[Aviator TRACE] Result : <Nil, null>
[Aviator TRACE] Result : <JavaType, A_0, 0, java.lang.Long>
[Aviator TRACE] Result : 0

org.opentest4j.AssertionFailedError: 
Expected :8
Actual   :0
<Click to see difference>


	省略异常栈...

由追踪模式输出的日志可知:

  1. 公式中的静态值 0.95 在系统中转换后,它被 Double 类型表示为了 0.9500000000000001
  2. 用计算结果的 0.95 去和 0.9500000000000001 做比较,得到的结果理所当然是 < 了,而不是 >=

2. 解决方案

AviatorScript 本身有考虑这种计算场景,可以通过配置,强制要求 AviatorScript 框架将整数和浮点数解析为 BigDecimal 类型,而不是坑爹的 Double 类型。

// -- 1. 解析浮点数为 Decimal 类型
AviatorEvaluator.getInstance().setOption(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, true);
// -- 2. 解析整数为 Decimal 类型
AviatorEvaluator.getInstance().setOption(Options.ALWAYS_PARSE_INTEGRAL_NUMBER_INTO_DECIMAL, true);

3. 验证

加上配置后再执行试试看

    @Test
    public void five(){
        String uiExpression = "if( 指标.完成值   /   指标.目标值   >=  0.95 ){ 8 }else{ 0 }";
        Map<String, Object> variable = new HashMap<>();
        variable.put("指标.目标值", new BigDecimal("100"));
        variable.put("指标.完成值", new BigDecimal("95"));

        // 是否跟踪运行,打开后将在控制台打印整个表达式的求值过程。请勿在生产环境打开,将极大地降低性能。默认为 false 关闭。
        AviatorEvaluator.getInstance().setOption(Options.TRACE_EVAL, true);

        // -- 1. 解析浮点数为 Decimal 类型
        AviatorEvaluator.getInstance().setOption(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, true);
        // -- 2. 解析整数为 Decimal 类型
        AviatorEvaluator.getInstance().setOption(Options.ALWAYS_PARSE_INTEGRAL_NUMBER_INTO_DECIMAL, true);

        // 执行表达式
        final Object execute = AviatorEvaluator.execute(uiExpression, variable);

        // 期望得到 8
        Assertions.assertEquals("8", execute.toString());
    }

执行输出结果是正常的,问题解决:

[Aviator TRACE]          <JavaType, 指标.完成值, 95, java.math.BigDecimal> / <JavaType, 指标.目标值, 100, java.math.BigDecimal> => <Decimal, 0.95>
[Aviator TRACE]          <Decimal, 0.95> >= <Decimal, 0.95> => <Boolean, true>
[Aviator TRACE] Func   : Lambda_1678191790762_57()
[Aviator TRACE] Tracing: null
[Aviator TRACE] Result : null
[Aviator TRACE] Result : <JavaType, A_0, 8, java.math.BigDecimal>
[Aviator TRACE] Func   : __if_callcc(<JavaType, A_0, 8, java.math.BigDecimal>,<Lambda, Lambda_1678191790762_59>)
[Aviator TRACE] Result : <Nil, null>
[Aviator TRACE] Result : <JavaType, A_0, 8, java.math.BigDecimal>
[Aviator TRACE] Result : 8
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值