Количество блоков для плевания диапазона значений свыше 64 - PullRequest
0 голосов
/ 01 апреля 2020

У меня есть следующий фрагмент кода:

long[] blocks = new long[(someClass.getMemberArray().length - 1) / 64 + 1];  

В основном someClass.getMemberArray() может возвращать массив, который может быть намного больше 64, и код пытается определить, сколько блоков len 64 необходимо для последующей обработки.
Я запутался в логи c и как это работает. Мне кажется, что просто делать:

 long[] blocks = new long[(int) Math.ceil(someClass.getMemberArray().length / 64.0)];  

должно сработать тоже, что выглядит проще.
Может кто-нибудь помочь мне понять рассуждения -1 и +1 в исходном фрагменте, как это работает и если ceil потерпит неудачу в некоторых случаях?

Ответы [ 2 ]

1 голос
/ 03 апреля 2020

Как вы правильно прокомментировали, -1 / + 1 требуется, чтобы получить правильное количество блоков, включая только частично заполненные. Он эффективно округляется.

(Но есть кое-что, что можно считать ошибкой: если массив имеет длину 0, что потребовало бы 0 блоков, он возвращает 1. Это происходит потому, что целочисленное деление обычно усекается в большинстве систем то есть округляет ВВЕРХ для отрицательных чисел, поэтому (0 - 1) / 64 возвращает 0. Однако это может быть особенностью, если нулевые блоки по каким-либо причинам не разрешены. Однако это обязательно требует комментария.)

Причина первой, оригинальной строки состоит в том, что она использует только целочисленную арифметику, которая должна переводиться только на несколько базовых c и быстрых машинных инструкций на большинстве компьютеров.

Второе решение включало приведение арифметики с плавающей точкой c и кастинг. Традиционно арифметика с плавающей точкой c была НАМНОГО медленнее на большинстве процессоров, что, вероятно, и послужило основанием для первого решения. Однако на современных процессорах с интегрированной поддержкой операций с плавающей запятой производительность больше зависит от других вещей, таких как строки кэша и конвейерная обработка.

Лично мне не очень нравятся оба решения, поскольку не совсем очевидно, что они делают , Поэтому я бы предложил следующее решение:

int arrayLength = someClass.getMemberArray().length;
int blockCount = ceilDiv(arrayLength, 64);
long[] blocks = new long[blockCount];

//...

/**
 * Integer division, rounding up.
 * @return the quotient a/b, rounded up.
 */
static int ceilDiv(int a, int b) {
    assert b >= 0 : b; // Doesn't work for negative divisor.

    // Divide.
    int quotient = a / b;

    // If a is not a multiple of b, round up.
    if (a % b != 0) {
        quotient++;
    }

    return quotient;
}

Это многословно, но по крайней мере понятно, что должно произойти, и оно дает общее решение, которое работает для всех целых чисел (кроме отрицательных делителей). К сожалению, большинство языков не предоставляют элегантного решения для округления целых чисел.

0 голосов
/ 01 апреля 2020

Я не понимаю, почему -1 был бы необходим, но +1, вероятно, там, чтобы исправить случай, когда результат деления округляется до ближайшего недесятичного значения (что, должно быть, хорошо, каждый случай, кроме тех, где результат без десятичных знаков)

...