【问题标题】:Java: double rounding algorithmJava:双舍入算法
【发布时间】:2015-05-08 00:59:42
【问题描述】:

我对舍入算法很好奇,因为在 CS 中我们必须在不使用数学库的情况下模拟 HP35。我们没有在最终构建中包含舍入算法,但我还是想这样做。

public class Round {
    public static void main(String[] args) {

        /*
         * Rounds by using modulus subtraction
         */
        double a = 1.123599;

        // Should you port this to another method, you can take this as a parameter
        int b = 5;

        double accuracy = Math.pow(10, -b);

        double remainder = a % accuracy;

        if (remainder >= 5 * accuracy / 10) // Divide by ten is important because remainder is smaller than accuracy
            a += accuracy;

        a -= remainder;


        /*
         * Removes round off error done by modulus
         */
        String string = Double.toString(a);

        int index = string.indexOf('.') + b;

        string = string.substring(0, index);

        a = Double.parseDouble(string);

        System.out.println(a);


    }
}

这是一个好的算法,还是有更好的算法?我不关心 Java API 中定义的那些,我只是想知道它是如何完成的。

[编辑] 这是我在查看 EJP 的答案后想出的代码

public class Round {
    public static void main(String[] args) {

        double a = -1.1234599;
        int b = 5;
        boolean negative = a < 0;

        if (negative) a = -a;

        String string = Double.toString(a);
        char array[] = string.toCharArray();

        int index = string.indexOf('.') + b;
        int i = index;

        int value;
        if (Character.getNumericValue(array[index +1]) >= 5) {

            for (; i > 0; i--) {
                value = Character.getNumericValue(array[i]);

                if (value != -1) {
                    ++value;
                    String temp = Integer.toString(value)
                    array[i] = temp.charAt(temp.length()-1);
                    if (value <= 9) break;
                }
            }
        }

        string = "";
        for (int j=0; j < index + 1 ; j++) {
            string += array[j];
        }

        a = Double.parseDouble(string);

        if (negative) a =-a;

        System.out.println(a);
    }
}

【问题讨论】:

    标签: java floating-point rounding


    【解决方案1】:

    浮点数没有小数位。它们有 binary 的位置,并且两者不可通约。任何将浮点变量修改为具有特定小数位数的尝试都注定要失败。

    在转换为十进制基数后,您必须将四舍五入到指定的小数位数。

    【讨论】:

    • 我的算法虽然有效(尽管不适用于底片)。他们以何种方式“注定失败”?
    • 您的算法适用于您测试过的几个值。尝试所有可能的值。它注定要失败,大多数时候,它不会产生精确到所需小数位数的值。这是不可能的。
    • 因此,如果我将双精度数转换为一个数组,其中每个元素都包含每个小数,然后使用数组进行四舍五入并携带每个溢出数字(即 .999 + .001 = .99 + .01 = .9 +.1) 单独,这是否类似于将其转换为十进制基数
    • is 转换为十进制基数,但您也可以使用 String.valueOf() and java.text.DecimalFormat 来实现。`
    【解决方案2】:

    对数字进行四舍五入有多种不同的方法。 Java 的RoundingMode 文档(在 1.5 中引入)应该简要介绍人们使用的不同方法。

    我知道您说过您无权访问 Math 函数,但您可以做的最简单的四舍五入是:

    public static double round(double d)
    {
        return Math.floor(d + 0.5);
    }
    

    如果你不想使用 任何 Math 函数,你可以试试这样的:

    public static double round(double d)
    {
        return (long)(d + 0.5);
    }
    

    这两者在某些情况下可能表现不同(负数?)。

    【讨论】:

    • 这些问题的问题在于,它们不允许用户定义他们想要的浮点精度。我提供的程序可以四舍五入到b小数点后位数
    • 你当然是对的。舍入到任意精度的流行方法是使用第一个示例中的逻辑:乘法、舍入、除法。如果您正在处理乘以 10^precision 时可能溢出的数字,这可能会导致问题。不过,我没有任何运气找到替代品。我唯一可以建议的是Newton's method - 连续添加/减去较小的值,直到它在给定精度的某个容差范围内。但是我不知道如何检查公差。
    • @AlexTaylor “流行方法”大部分时间都失败了。请参阅here 以获取证据。
    • 哦,我相信。我已经为 BSP 树分割了足够多的多边形,因此从不相信任何重要的浮点计算。
    • 如果你打算使用Math.floor,你也可以使用Math.round。此外,这两个只四舍五入到整数,而不是到一定数量的小数位,这就是问题所在。
    猜你喜欢
    • 2015-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多