2

I want to round a double to the closest odd mathematical integer (long) in Java. Here is a test program:

public class RoundToOdd {

    public static void main(String[] args) {
        System.out.println(roundToOdd(2.1)); // expected: 3
        System.out.println(roundToOdd(4.9)); // expected: 5
        System.out.println(roundToOdd(3.7)); // expected: 3
        System.out.println(roundToOdd(1.1)); // expected: 1
        System.out.println(roundToOdd(7.0)); // expected: 7
        System.out.println(roundToOdd(2.0)); // expected: 1 or 3, depends on requirements
    }

    public static long roundToOdd(double d) {
        return ...;
    }
}
Daniel S.
  • 6,458
  • 4
  • 35
  • 78
  • I'm wondering why this question isn't already on SO. – Daniel S. Jul 17 '22 at 06:51
  • 4
    It's very specific and not too practical. What have you tried? – matt Jul 17 '22 at 06:54
  • 1
    @matt sure I can write it myself. You can surely tell that from my points and other answers. I simply posted this question because SO lacks it, but the analogous question about rounding to even is present. Feel free to answer and get the points. – Daniel S. Jul 17 '22 at 06:58
  • 1
    Seems more like truncation than rounding, since you ignore the fractional part entirely. What do you expect as the result of an input of 2.0? – Basil Bourque Jul 17 '22 at 07:02
  • "...but the analogous question about rounding to even is present. " Sounds like this is a duplicate then. Are the differences between rounding to the nearest even number or rounding to the nearest odd number so great? – matt Jul 17 '22 at 07:03
  • @matt Well, if you look at the fact that the first answer to this question initially had a bug, it seems not too trivial. And yes, there is a substancial difference which makes rounding to odd slightly more complex than rounding to even. Just look at the corrected first answer to see that. – Daniel S. Jul 17 '22 at 07:09
  • 1
    and WHAT are the "requirements"??? ("1 or 3" is not a value that can be represented by `long` :- ) || BTW simple math: `return 1 + 2 * Math.round((d-1)/2);` – user16320675 Jul 17 '22 at 07:22
  • 1
    By the way, you can not round exactly big numbers, they will have some rounding-errors. – user-id-14900042 Jul 17 '22 at 07:22
  • @BasilBourque It's not trucation. E.g. 4.9 gets rounded to 5. – Daniel S. Jul 18 '22 at 07:27
  • My question is very literal, What is wrong with the current answer? I'm not asking it to "be right" – matt Jul 18 '22 at 08:02

2 Answers2

2

The following function solves all the test cases listed at the end of this post.

public static long roundToOdd(double d) {
    if (d > Long.MAX_VALUE) {
        return Long.MAX_VALUE;
    } else if (d <= Long.MIN_VALUE) {
        return Long.MIN_VALUE + 1;
    }

    return Math.round((d + 1.0) / 2.0) * 2 - 1;
}

The most important part of this function is the formula Math.round((d + 1.0) / 2.0) * 2 - 1, which calculates the result for all finite numbers d with

Long.MIN_VALUE < d <= Long.MAX_VALUE or d being NaN (not a number).

Math.round((d + 1.0) / 2.0) * 2 - 1 first translates the number d to one higher (d + 1.0) and then rounds to an even number by doing the steps /2, rounding to integer, and *2 in this order. This result is too high by 1, because we added 1 before rounding, so in the end we have to do -1 to reach the correct odd number.
If d is NaN, then the result is -1, because Math.round(Double.NaN) results in 0.

The if's test for special cases, which are all values higher than Long.MAX_VALUE or lower than or equal to Long.MIN_VALUE (this includes +Infinity / -Infinity). These are special cases, because no matter how far d is below Long.MIN_VALUE, the closest odd long value to d then is Long.MIN_VALUE + 1, or respectively no matter how far d is above Long.MAX_VALUE, the closest odd long value then is Long.MAX_VALUE.

Test cases:

-Infinity -> -9223372036854775807 ✔️ (expected -9223372036854775807)
-9223372036854775808.0 -> -9223372036854775807 ✔️ (expected -9223372036854775807)
-1152921504606846976.0 -> -1152921504606846977 ✔️ (expected one of [-1152921504606846977, -1152921504606846975])
-9007199254740994.0 -> -9007199254740993 ✔️ (expected one of [-9007199254740995, -9007199254740993])
-9007199254740992.0 -> -9007199254740991 ✔️ (expected one of [-9007199254740993, -9007199254740991])
-7.0 -> -7 ✔️ (expected -7)
-4.9 -> -5 ✔️ (expected -5)
-3.7 -> -3 ✔️ (expected -3)
-2.1 -> -3 ✔️ (expected -3)
-2.0 -> -1 ✔️ (expected one of [-1, -3])
-1.1 -> -1 ✔️ (expected -1)
-0.1 -> -1 ✔️ (expected -1)
0.0 -> 1 ✔️ (expected one of [-1, 1])
0.1 -> 1 ✔️ (expected 1)
1.1 -> 1 ✔️ (expected 1)
2.0 -> 3 ✔️ (expected one of [1, 3])
2.1 -> 3 ✔️ (expected 3)
3.7 -> 3 ✔️ (expected 3)
4.9 -> 5 ✔️ (expected 5)
7.0 -> 7 ✔️ (expected 7)
9007199254740992.0 -> 9007199254740991 ✔️ (expected one of [9007199254740991, 9007199254740993])
9007199254740994.0 -> 9007199254740995 ✔️ (expected one of [9007199254740993, 9007199254740995])
1152921504606846976.0 -> 1152921504606846975 ✔️ (expected one of [1152921504606846975, 1152921504606846977])
9223372036854775808.0 -> 9223372036854775807 ✔️ (expected 9223372036854775807)
Infinity -> 9223372036854775807 ✔️ (expected 9223372036854775807)
NaN -> -1 ✔️ (expected -1)
Daniel S.
  • 6,458
  • 4
  • 35
  • 78
1

This should do it:

public class RoundToOdd {

    public static void main(String[] args) {
        System.out.println(roundToOdd(2.1)); // expected: 3
        System.out.println(roundToOdd(4.9)); // expected: 5
        System.out.println(roundToOdd(3.7)); // expected: 3
        System.out.println(roundToOdd(1.1)); // expected: 1
        System.out.println(roundToOdd(2.0));
        System.out.println(roundToOdd(1.0));
    }

    public static long roundToOdd(double d) {
        long value = 1l;
        if(Math.floor(d) % 2 == 0) {
             if(d % 2 != 0) {
               value = (long) Math.ceil(d);
             } else if (d > Long.MAX_VALUE) {
               value = Long.MAX_VALUE;
             } else {
               value = (long) d - 1;
             }
        } else {
          value = (long) Math.floor(d);
        }
        return value;
    }
}

Updated the answer to handle the number which are even, as pointed out in the comment.

Charchit Kapoor
  • 8,934
  • 2
  • 8
  • 24