1

I got curious about a rounding algorithm, because in CS we had to emulate an HP35 without using the Math library. We didn't include a rounding algorithm in our final build, but I wanted to do it anyway.

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);


    }
}

Is this a good algorithm, or are there any better ones? I don't care about the ones defined in the Java API, I just wanted to know how it was done.

[EDIT] Here's the code I came up with after looking over EJP's answer

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);
    }
}
Lightfire228
  • 546
  • 1
  • 5
  • 17

2 Answers2

5

Floating-point numbers don't have decimal places. They have binary places, and the two are not commensurable. Any attempt to modify a floating-point variable to have a specific number of decimal places is doomed to failure.

You have to do the rounding to a specified number of decimal places after conversion to a decimal radix.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • My algorithm works though (albeit not for negatives). In what way are they "**doomed to failure**"? – Lightfire228 May 08 '15 at 01:38
  • 1
    Your algorithm works for the few values you've tested it with. Try it with all possible values. It is doomed to failure in that, *most of the time*, it won't produce values with exactly the desired number of decimal places. It's impossible. – user207421 May 08 '15 at 01:40
  • So, if I convert the double into an array in which each element contains each decimal, then round using the array and carrying each overflow digit (i.e. .999 + .001 = .99 + .01 = .9 +.1) individually, would that be similar to converting it to a _decimal radix_? – Lightfire228 May 08 '15 at 02:34
  • 1
    That *is* converting to a decimal radix, but you can also do it with `String.valueOf() and `java.text.DecimalFormat.` – user207421 May 08 '15 at 02:41
0

There are a different ways to round numbers. The RoundingMode documentation for Java (introduced in 1.5) should give you a brief introduction to the different methods people use.

I know you said you don't have access to the Math functions, but the simplest rounding you can do is:

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

If you don't want to use any Math functions, you could try something like this:

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

Those two probably behave differently in some situations (negative numbers?).

Alex Taylor
  • 8,343
  • 4
  • 25
  • 40
  • The problem with these though, is that they don't allow the user to define the accuracy they want the floating point to be. The program I provided can round to `b` digits after the decimal – Lightfire228 May 08 '15 at 01:30
  • You are, of course, right. The popular method of rounding to an arbitrary precision is to use logic like your first example: multiply up, round, divide back down. That can cause issues if you're dealing with numbers that are likely to overflow when multiplied up by 10^precision. I haven't had any luck finding alternatives though. The only thing I could suggest is something like [Newton's method](http://en.wikipedia.org/wiki/Newton%27s_method) - add/subtract successively smaller values until it is within some tolerance of a given precision. I don't know how to check the tolerance however. – Alex Taylor May 08 '15 at 01:44
  • @AlexTaylor The 'popular method' fails most of the time. See [here](http://stackoverflow.com/a/7593617/207421) for proof. – user207421 May 08 '15 at 01:48
  • Oh I believe it. I've split enough polygons for BSP trees to never trust a floating point calculation for anything that matters. – Alex Taylor May 08 '15 at 01:53
  • If you're going to use `Math.floor` you might just as well use `Math.round`. Besides, these two only round to integers and not to a certain number of decimal places which is what the question was about. – Raniz May 08 '15 at 04:11