【问题标题】:Consistent evaluation and match the fractional values一致的评估和匹配小数值
【发布时间】:2022-01-20 05:51:24
【问题描述】:

我有一个 Web 应用程序,搜索由 ElasticSearch 提供支持。

我在 Java 中使用 ScriptEngine 计算小数值:

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
System.out.println("12*0.0254 = " + engine.eval("12*0.0254"));//0.30479999999999996
System.out.println("12000*0.0000254 = " + engine.eval("12000*0.0000254"));//0.3048

从 cmets 的 SOP 输出中可以看出,对于相同的输入,输出的四舍五入方式不同。

在 UI 中,我使用 JavaScript 的内置 eval() 函数,这与不一致的 Java 输出不匹配。

Java 中获得一致且正确的表达式结果的方法是什么,即12 * 0.0254 应始终评估为0.3048 而不是0.30479999999999996

【问题讨论】:

    标签: javascript java fractions


    【解决方案1】:

    仅仅因为两个表达式在数学上是等价的,并不意味着它们在给定的机器算术中计算为相同的值。

    Java 虚拟机包含浮点数的子集 IEEE 二进制浮点标准中规定的算法 算术(ANSI/IEEE 标准 754-1985,纽约)。

    2.8.1。 Java 虚拟机浮点运算和 IEEE 754

    Java 虚拟机支持的浮点运算与 IEEE 754 标准的主要区别是:

    Java 虚拟机的浮点运算不会抛出 > 异常、陷阱或以其他方式发出 IEEE 754 异常条件的信号 > 无效操作、被零除、溢出、下溢或不精确。 >Java 虚拟机没有信号 NaN 值。

    Java 虚拟机不支持 IEEE 754 信号浮点比较。

    Java 虚拟机的舍入操作始终使用 IEEE 754 >round to nearest 模式。不精确的结果被四舍五入到最接近的 > 可表示的值,并与具有零最低有效位的值相联系。这是 IEEE 754 默认模式。但是 Java 虚拟机 > 将浮点类型的值转换为 > 整数类型的值的指令向零舍入。 Java 虚拟机没有提供任何改变浮点舍入模式的方法。

    Java 虚拟机不支持 IEEE 754 单扩展或双扩展格式,除非可以说双和双扩展指数值集支持单扩展格式。可以选择支持的 float-extended-exponent 和 double-extended-exponent 值集与 IEEE 754 扩展格式的值不对应:IEEE 754 扩展格式需要扩展精度和扩展指数范围。

    来源:https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-2.html#jvms-2.8.1

    在 JavaScript 中,我得到了和你一样的结果(https://jsfiddle.net/,Firefox 开发者版 97.0b5(64 位):

    console.log(12*0.0254);
    console.log(12000*0.0000254);
    
    0.30479999999999996
    0.3048
    

    也许您的 GUI 框架应用了舍入?

    您可以像这样在 Java 中将输出格式化为一定数量的小数位:

    DecimalFormat df = new DecimalFormat("#.#####");
    System.out.println(df.format(12*0.0254));
    System.out.println(df.format(12000*0.0000254));
    

    【讨论】:

    • Java 中始终将12*0.0254 评估为0.3048 的解决方案是什么?
    • @RRM:正如我所写,这不仅仅是 Java 的问题,它在 JavaScript 中的发生方式相同。您可以将其显示为四舍五入,例如使用 ` DecimalFormat df = new DecimalFormat("#.#####"); System.out.println(df.format(12*0.0254)); System.out.println(df.format(12000*0.0000254)); `
    • 能否请您将此 DecimalFormat sn-p 放在您的主要回复中,以便我接受它作为解决方案?
    • @RRM:我按要求在答案中添加了 sn-p。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-13
    • 1970-01-01
    • 2012-05-10
    相关资源
    最近更新 更多