2016-07-22 3 views
3

Я столкнулся с неожиданностью с целым делением не округление, как ожидалось.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)

+0

Возможно, результаты будут разными, если вы фактически разделите 'div', а не' s'. Я имею в виду, что вы создали «div» по какой-то причине, я полагаю. – Andreas

+0

Когда '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? – Andreas

ответ

3

Целочисленное деление в Java не округляет вниз, как вы его понимаете (то есть на более низкое значение.). В терминологии Java округление означает rounding towards zero.

Here is the relevant section from the JLS, который прямо говорит

целочисленное деление раундов в направлении 0.

Самый простой способ получить округление вы хотите сделать разделение с плавающей точкой, вместо этого, и использовать Math.floor округлить.

int res = (int)Math.floor(-8F/s); 
+1

Согласно терминологии Java, «округление вниз» * равно * к нулю. См. ['RoundingMode'] (https://docs.oracle.com/javase/7/docs/api/java/math/RoundingMode.html#enum_constant_summary): **' DOWN': режим округления для округления к нулю ** , 'UP': режим округления для округления от нуля,' FLOOR': режим округления до округления к отрицательной бесконечности, 'CEILING': округление до округления к положительной бесконечности. – Andreas

+0

Очень хорошо +1. Теперь, для полного ответа, объясните OP, почему * «Я ожидал -1 полностью вниз» * - это ошибочное ожидание. Подсказка: разделение на 's', а не' div', и даже тогда, для 's = 1' /' div = 1 << s = 2', вы все равно не получите -1, используя 's' или 'div'. – Andreas

2

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

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

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

+1

Спецификация Java, подтверждающая ваше утверждение: [JLS §5.1.3] (https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.3) * .. значение с плавающей запятой округляется до целочисленного значения V, округляя до нуля, используя режим IEEE 754 round-to-zero ([§4.2.3] (https://docs.oracle.com/javase/specs/jls/ SE8/html/JLS-4.html # ПСБ-4.2.3)) *. – Andreas

Смежные вопросы