Функция Java Power отвечает - PullRequest
0 голосов
/ 09 июня 2018

Я писал свою собственную реализацию функции power и обнаружил некоторые странные результаты, которые происходят в районе Integer.MAX_VALUE, но я не уверен, почему они происходят.Это моя реализация:

public static long power(long x, long y) {
    int result = 1;
    while (y > 0) {
        if ((y & 1) == 0) {
            x *= x;
            y >>>= 1;
        } else {
            result *= x;
            y--;
        }
    }
    return result;
}

Следующий код запускается,

System.out.println(fastPower(2, 31));
System.out.println(Math.pow(2, 31);
System.out.println((long)Math.pow(2, 31));
System.out.println((int)Math.pow(2, 31));

Результаты следующие, которых я не понимаю.

-2147483648
2.147483648E9
2147483648
2147483647

Это еще больше смущает меня, когда используются short s:

System.out.println(fastPower(2, 15));
System.out.println(Math.pow(2, 15));
System.out.println((int)Math.pow(2, 15));
System.out.println((short)Math.pow(2,15));

32768
32768.0
32768
-32768

Это ответы, которые я ожидал, но они кажутся несовместимыми с результатами ints.

Ответы [ 2 ]

0 голосов
/ 09 июня 2018

Предполагая, что power() и fastPower() одинаковы, fastPower(2, 31) возвращает -2147483648, поскольку переменная result равна int, хотя параметры и тип возвращаемого значения long.

Math.pow() возвращает double, поэтому приведение результата к целочисленному типу (long, int, short, byte, char) соответствует правилам JLS 5.1.3.Сужение примитивного преобразования , приведенное ниже.

Math.pow(2, 31) - это 2147483648.0.При приведении к long это то же значение, то есть 2147483648.Однако при приведении к int значение слишком велико, поэтому результат равен Integer.MAX_VALUE, то есть 2147483647, как показано в приведенной ниже цитате.

Math.pow(2, 15) равно 32768.0.При приведении к int это то же значение, то есть 32768.Однако при приведении к short значение сначала сужается до int, , затем сужается до short путем отбрасывания старших битов (см. Вторую цитату ниже), что приводит к числовому значениюпереполнение до -32768.

Сужающее преобразование числа с плавающей запятой в целочисленный тип T выполняется в два этапа:

  1. На первом этапе число с плавающей запятой преобразуется в long, если T равно long, или в int, если T равно byte, short, char или int, следующим образом:

    • Если число с плавающей запятой равно NaN ( §4.2.3 ), результатпервый шаг преобразования - int или long 0.

    • В противном случае, если число с плавающей запятой не является бесконечностью, значение с плавающей запятой округляетсядо целочисленного значения V, округления до нуля с использованием режима округления до нуля IEEE 754 ( §4.2.3 ).Тогда есть два случая:

      1. Если T равен long, и это целочисленное значение может быть представлено как long, то результатом первого шага будет longзначение V.

      2. В противном случае, если это целочисленное значение может быть представлено как int, то результатом первого шага будет int значение V.

    • В противном случае один из следующих двух случаев должен быть истинным:

      1. Значение должно быть слишком маленьким (отрицательное значениезначение большой величины или отрицательной бесконечности), а результатом первого шага является наименьшее представимое значение типа int или long.

      2. Значение должно бытьслишком большое (положительное значение большой величины или положительная бесконечность), и результатом первого шага является наибольшее представимое значение типа int или long.

  2. На втором шаге:

    • Если T равно int или long, результат преобразованияsion является результатом первого шага.

    • Если T равно byte, char или short, результат преобразования является результатом сужающегося преобразования ввведите T ( §5.1.3 ) результата первого шага.

Сужающее преобразованиецелое число со знаком для целочисленного типа T просто отбрасывает все биты, кроме n младших разрядов, где n - это число битов, используемых для представления типа T. В дополнение к возможной потереИнформация о величине числового значения может привести к тому, что знак полученного значения будет отличаться от знака входного значения.

0 голосов
/ 09 июня 2018

Первые три выхода из int и short легко объяснить:

-2147483648 // your method returns an int, so overflows
2.147483648E9 // Math.pow returns a double, so formatted like this
2147483648 // double casted to a long, 2147483648 inside the possible range for long

32768 // your method returns an int, 32768 is inside the possible range for int
32768.0 // Math.pow returns a double, so formatted like this
32768 // double casted to an int, 32768 is inside the possible range for int

Сложный для объяснения бит - четвертый результат.Не следует ли System.out.println((int)Math.pow(2, 31)); печатать -2147483648?

Хитрость здесь в том, как Java выполняет преобразование из double в int.Согласно спецификации, это известно как сужающее примитивное преобразование ( §5.1.3 ):

22 конкретные преобразования на примитивных типах называются сужающими примитивными преобразованиями:

  • short to byte или char
  • char to byte или short
  • int в byte, short или char
  • long в byte, short, charили int
  • с плавающей запятой в байтах, коротких, char, int или long
  • с двойным байтом, короткими, символами, int, long или float

Вот как выполняется преобразование double в int (выделено мной):

1.На первом шаге число с плавающей запятой преобразуется либо в long, если T long, либо в int, если T байтовое, короткое, char или int, следующим образом:

  • Если число с плавающей запятой равно NaN (§4.2.3), результатом первого шага преобразования будет int или long 0.
  • В противном случае, если число с плавающей запятой равноне бесконечность, значение с плавающей запятой округляется до целочисленного значения V с округлением до нуля с использованием режима округления до нуля IEEE 754 (§4.2.3).Тогда есть два случая:

a.Если T длинный, и это целочисленное значение может быть представлено как long, то результатом первого шага будет длинное значение V. b.В противном случае, если это целочисленное значение может быть представлено как int, то результатом первого шага будет значение int V.

  • В противном случае один из следующих двух случаев долженбыть правдой: a.Значение должно быть слишком маленьким (отрицательное значение большой величины или отрицательная бесконечность), а результатом первого шага является наименьшее представимое значение типа int или long. б.Значение должно быть слишком большим (положительное значение большой величины или положительная бесконечность), и результатом первого шага является наибольшее представимое значение типа int или long.

  • На втором шаге:
  • Если T является int или long, результат преобразования является результатом первого шага.

  • Если T является байтом, символом или коротким, результатом преобразования является результат сужающего преобразования в тип T (§5.1.3) результата первого шага.

Первый шаг изменяет значение double на наибольшее представимое значение int - 2147483647. Вот почему в случае int печатается 2147483647.В случае short, второй шаг изменяет значение int 2147483647 на short, например:

Сужающее преобразование целого числа со знаком в целочисленный тип T просто отбрасываетвсе, кроме n младших битов, где n - количество битов, используемых для представления типа T.

Вот почему short перелетел, но int не сделал!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...