Целочисленное округление Java (связанное с делением)

Я столкнулся с сюрпризом, когда целочисленное деление не округляется в меньшую сторону, как ожидалось.

Простой код:

public class HelloMath {

    public static void main(String[] args) {
        for (int s=1; s< 20; s++)
        {
            int div = 1<<s;
            int res = (int) ((float)-8/ s);
            System.out.printf("Bit %d, result %d\n", s, res);
        }
    }

}

Даже с явными (плавающими) приведениями вывод:

Bit 1, result -8
Bit 2, result -4
Bit 3, result -2
Bit 4, result -2
Bit 5, result -1
Bit 6, result -1
Bit 7, result -1
Bit 8, result -1
Bit 9, result 0
Bit 10, result 0
Bit 11, result 0
Bit 12, result 0
Bit 13, result 0
Bit 14, result 0
Bit 15, result 0
Bit 16, result 0
Bit 17, result 0
Bit 18, result 0
Bit 19, result 0

Я ожидал -1 на всем пути вниз.

Реальный код, в котором это происходит, делает следующее:

public static int fluidTo8th(int fluid)
{
    if (0 == fluid)
        return 0;   // Technically, this isn't needed :-).
    int wholePart = (fluid-1) * 8 / RealisticFluids.MAX_FLUID; // -1 gets rounding correct;
    // consider fluid of exactly 1/8th.
    return 1+wholePart;
}

RealisticFluids.MAX_FLUID имеет значение (1‹‹20). Предполагается, что код принимает ввод от 1 до MAX_FLUID (0 должен происходить только в том случае, если входной блок является воздухом) и возвращает число от 0 до 7 — от 1 до 1/8 макс. равно 0, 1/8 +1 до 2/8 - 2, до 7/8 + 1 до 8/8 - 7.

Я ожидаю, что целочисленная математика округлит все дроби вниз. Но этого не происходит - я получаю ошибки по одному везде, так как 5,999 становится 6 вместо 5.

  1. Где задокументировано такое поведение?
  2. Какой самый простой обходной путь, чтобы получить ожидаемое округление?

(полный контекст кода: https://github.com/keybounce/Finite-Fluids/blob/master/src/main/java/com/mcfht/realisticfluids/Util.java)


person Keybounce    schedule 22.07.2016    source источник
comment
Возможно, результаты будут другими, если вы действительно разделите на div, а не на s. Я имею в виду, вы действительно создали div по какой-то причине, я полагаю.   -  person Andreas    schedule 22.07.2016
comment
Когда s = 1, деление -8 на s равно -8, а деление -8 на div (1‹‹1=2) равно -4, так почему вы ожидаете, что будет -1? --- Когда s = 9, деление -8 на s равно 0 (округлено вниз от -0,8888889), а деление -8 на div (1‹‹9=512) равно 0 (округлено вниз от -0,015625), так почему вы ожидаете должно быть -1?   -  person Andreas    schedule 22.07.2016


Ответы (1)


Я ожидаю, что целочисленная математика округлит все дроби вниз.

Прежде всего, вы не выполняете целочисленное деление: вы выполняете деление с плавающей запятой. (float)-8 — это выражение с плавающей запятой. Следовательно, s в (float)-8/s повышается до (с плавающей запятой) до того, как произойдет деление.

Затем результат с плавающей запятой преобразуется в целое число. Ройсеман сказал, что целочисленное деление округляется до нуля. Что ж, преобразование float->int также округляется до нуля.

person Solomon Slow    schedule 22.07.2016
comment
Спецификация Java, подтверждающая ваше утверждение: JLS §5.1.3 ... значение с плавающей запятой округляется до целого значения V, округляя до нуля с использованием режима округления до нуля IEEE 754 (§4.2.3). - person Andreas; 22.07.2016