Разница между Long.toBinaryString() и Long.toString(num, 2)?

#java

#java

Вопрос:

Обе эти функции преобразуют Long в двоичную строку, но является ли одна более оптимизированной, чем другая? Я просмотрел исходный код для класса Long, и эти два статических метода имеют разные реализации.

Комментарии:

1. Я был бы шокирован, если toBinaryString бы не был оптимизирован для этой задачи.

2. toString использует операции остатка и деления для построения цифр, но toBinaryString использует сдвиги вправо. Но я не уверен, оптимизирует ли компилятор деление на и и остаток от 2, используя сдвиги. Так что я не могу сказать, будет ли какая-то существенная разница. Если оптимизация компилятора выполнена, я ожидаю, что они будут очень похожи. Просто мои мысли, не уверен, правда это или нет.

3. Из быстрого теста (все еще не с анализом JMH / JIT, но придерживаясь некоторых методов микробеншмарк) кажется, что «основной» цикл преобразования (который записывает цифры в char буфер) может быть только на ~ 25% медленнее для универсального toString метода. Но значительные дополнительные накладные расходы могут быть связаны с тем фактом, что в toString методе соответствующая часть char массива должна быть скопирована при создании строки. В toBinaryString , количество требуемых символов вычисляется напрямую , и результирующий char массив передается в строку без его копирования.

Ответ №1:

Ответ легко получить, просто проведите тест:

 class Test {
  public static void main (String args[]) {
    long millisStart = System.currentTimeMillis();
    for(int i = 0; i < 1000000; i  ) {
        String s = Long.toBinaryString(i / 10000);
    }
    System.out.println(System.currentTimeMillis() - millisStart);

    millisStart = System.currentTimeMillis();
    for(int i = 0; i < 1000000; i  ) {
        String st = Long.toString(i / 10000, 2);
    }
    System.out.println(System.currentTimeMillis() - millisStart);
  }
}
  

В системе, на которой я тестировал это (Raspberry Pi), это приводит к 655 против 3279.

Итак, я бы предположил, что Long.toString(num, 2); медленнее. Что неудивительно, поскольку она должна быть гибкой.

Комментарии:

1. (Я не сторонник отрицательного мнения, но) хотя надлежащий бенчмарк, скорее всего, пришел бы к аналогичному выводу, бенчмарк в данной форме никоим образом НЕ является надежным.

2. Не должен был быть надлежащим эталоном. Однако, когда дело доходит до величин различий в сочетании со специализацией для определенных функций, он по-прежнему дает четкий ответ на вопрос «является ли один более оптимизированным, чем другой?».

3. Спасибо за тест, Флориан. Я больше смотрю, может ли кто-нибудь заглянуть в исходный код и объяснить, ПОЧЕМУ toBinaryString более оптимизирован, предполагая, что это так. (Кстати, я не сторонник downvoter)

4. @user2603017 Не беспокойтесь, помогает прояснить суть вопроса. Как намекнул Армин Дж, оптимизаторы могут оказать здесь немалое влияние. Компиляторы Jit, как правило, ведут себя по-разному в зависимости от платформы.

Ответ №2:

Основное отличие заключается в том, как отображаются отрицательные числа.

 var s = System.out;
Long oL = 0L;
long num = -1;

s.println(oL.toString(num, 2) );     //binary result
s.println(oL.toBinaryString(num) );

s.println("0x" oL.toHexString(num).toUpperCase() );
  

Вывод:

 -1
1111111111111111111111111111111111111111111111111111111111111111
0xFFFFFFFFFFFFFFFF
  

toString() использует математическую нотацию.

toBinaryString() учитывает способ, которым отрицательные числа фактически реализованы в аппаратном обеспечении.