参考链接
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>
省略异常栈...
由追踪模式输出的日志可知:
- 公式中的静态值
0.95
在系统中转换后,它被Double
类型表示为了0.9500000000000001
。 - 用计算结果的
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