Почему Java не поддерживает unsigned ints?

Почему Java не поддерживает поддержку целых чисел без знака?

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

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

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

В чем недостаток их включения?

Ответы

Ответ 1

Это из интервью с Гослинг и другими, о простоте:

Гослинг: для меня, как разработчика языка, который я на самом деле не считаю себя в наши дни, то, что "простое" действительно в конечном итоге означало, я мог ожидать, что J. Random Developer проведет спецификацию в его голове. В этом определении говорится, что, например, Java не является - и на самом деле многие из этих языков имеют множество угловых случаев, которые никто не понимает. Попробуйте любой разработчик C о неподписанном, и довольно скоро вы обнаружите, что почти ни один разработчик C не понимает, что происходит с unsigned, что означает арифметика без знака. Такие вещи сделали сложным. Языковая часть Java, я думаю, довольно проста. Библиотеки, которые вам нужно найти.

Ответ 2

Чтение между строками, я думаю, что логика была примерно такой:

  • разработчики Java хотели упростить репертуар доступных типов данных
  • для повседневных целей они считали, что наиболее распространенная потребность в подписанных типах данных
  • для реализации определенных алгоритмов иногда требуется арифметика без знака, но тип программистов, которые будут реализовывать такие алгоритмы, также имел бы знание "работать", делая беззнаковое арифметическое со стандартными типами данных

В основном, я бы сказал, что это разумное решение. Возможно, я бы:

  • сделал байты unsigned или, по крайней мере, предоставил подписанные/неподписанные альтернативы, возможно с разными именами, для этого одного типа данных (что делает его подписанным для последовательности, но когда вам когда-нибудь нужен подписанный байт?)
  • покончено с "коротким" (когда вы в последний раз использовали 16-разрядную арифметику?)

Тем не менее, при небольшом клонировании операции с неподписанными значениями до 32 бит не являются tooo bad, и большинству людей не требуется 64-разрядное деление или сравнение без знака.

Ответ 3

Это более старый вопрос, и вкратце кратко упомянул char, я просто подумал, что я должен расширить это для других, которые будут смотреть на это по дороге. Давайте более подробно рассмотрим примитивные типы Java:

byte - 8-разрядное целое число со знаком

short - 16-разрядное целое число со знаком

int - 32-разрядное целое число со знаком

long - 64-разрядное целое число со знаком

char - 16-разрядный символ (целое без знака)

Хотя char не поддерживает арифметику unsigned, ее по существу можно рассматривать как целое число unsigned. Вам нужно было бы явно передать арифметические операции в char, но это дает вам возможность указать числа unsigned.

char a = 0;
char b = 6;
a += 1;
a = (char) (a * b);
a = (char) (a + b);
a = (char) (a - 16);
b = (char) (b % 3);
b = (char) (b / a);
//a = -1; // Generates complier error, must be cast to char
System.out.println(a); // Prints ? 
System.out.println((int) a); // Prints 65532
System.out.println((short) a); // Prints -4
short c = -4;
System.out.println((int) c); // Prints -4, notice the difference with char
a *= 2;
a -= 6;
a /= 3;
a %= 7;
a++;
a--;

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


Изменить

С JDK8 появились новые API для long и Integer, которые предоставляют вспомогательные методы при обработке значений long и int как значения без знака.

  • compareUnsigned
  • divideUnsigned
  • parseUnsignedInt
  • parseUnsignedLong
  • remainderUnsigned
  • toUnsignedLong
  • toUnsignedString

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

Ответ 4

У Java есть неподписанные типы или, по крайней мере, один: char - это unsigned short. Так что, что бы ни оправдало Гослинга, это действительно просто его невежество, почему нет других неподписанных типов.

Также короткие типы: шорты используются все время для мультимедиа. Причина в том, что вы можете поместить 2 образца в один 32-разрядный беззнаковый длинный и векторизовать многие операции. То же самое с 8-битными данными и беззнаковым байтом. Вы можете поместить 4 или 8 выборок в регистр для векторизации.

Ответ 5

Как только подписанные и unsigned ints смешаны в выражении, все начинает становиться беспорядочным, и вы, вероятно, потеряете информацию. Ограничение Java на подписанные ints только реально очищает вещи. Im рад, что мне не нужно беспокоиться о всем подписанном/неподписанном бизнесе, хотя иногда я пропускаю 8-й бит в байте.

Ответ 6

http://skeletoncoder.blogspot.com/2006/09/java-tutorials-why-no-unsigned.html

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

Ответ 7

Я думаю, что Java в порядке, добавив unsigned, это усложнит его без большой выгоды. Даже с упрощенной целочисленной моделью большинство программистов на Java не знают, как ведут себя основные числовые типы - просто прочитайте книгу Java Puzzlers, чтобы увидеть, что неправильные представления, которые вы можете придерживаться.

Что касается практических советов:

  • Если ваши значения несколько произвольного размера и не вписываются в int, используйте long. Если они не вписываются в long, используйте BigInteger.

  • Используйте меньшие типы только для массивов, когда вам нужно сэкономить место.

  • Если вам нужны ровно 64/32/16/8 бит, используйте long/int/short/byte и перестаньте беспокоиться о знаке, за исключением деления, сравнения, прав сдвиг и кастинг.

См. также этот ответ о "переносе генератора случайных чисел с C на Java".

Ответ 8

С JDK8 у него есть определенная поддержка для них.

Мы все еще можем видеть полную поддержку неподписанных типов в Java, несмотря на озабоченность Гослинга.

Ответ 9

Я знаю, что этот пост слишком стар; однако для вашего интереса, в Java 8 и более поздних версиях, вы можете использовать тип данных int для представления 32-разрядного целого числа без знака, которое имеет минимальное значение 0 и максимальное значение 2 32 -1. Используйте класс Integer, чтобы использовать тип данных int как целое число без знака, а статические методы, такие как compareUnsigned(), divideUnsigned() и т.д., Были добавлены в класс Integer для поддержки арифметических операций для целых чисел без знака.

Ответ 10

Я слышал рассказы о том, что они должны быть включены близко к выпуску Java orignal. Дуб был предшественником Java, и в некоторых спецификационных документах упоминалось о присвоенных значениях. К сожалению, они никогда не попадали в язык Java. Насколько кому-то удалось выяснить, они просто не реализовались, вероятно, из-за ограничения времени.

Ответ 11

Я как-то взял курс С++ с кем-то в комитете по стандартам С++, который подразумевал, что Java приняла правильное решение избежать целых чисел без знака, потому что (1) большинство программ, которые используют целые числа без знака, могут делать так же хорошо с целыми целыми числами, и это более естественным с точки зрения того, как люди думают, и (2) использование целых чисел без знака приводит к легкому созданию, но трудно отлаживать такие проблемы, как целочисленное арифметическое переполнение и потеря значительных бит при конвертации между подписанными и неподписанными типами. Если вы ошибочно вычтите 1 из 0 с использованием целых чисел со знаком, это часто приводит к сбою вашей программы и облегчает поиск ошибки, чем если она обертывается до 2 ^ 32-1, а компиляторы и инструменты статического анализа и проверки времени выполнения должны предположим, что вы знаете, что делаете, так как вы решили использовать арифметику без знака. Кроме того, отрицательные числа, такие как -1, могут часто представлять что-то полезное, например, поле игнорируется/дефолт/не задано, а если вы используете unsigned, вам нужно зарезервировать специальное значение, например 2 ^ 32 - 1 или что-то подобное.

Давным-давно, когда память была ограничена, и процессоры автоматически не работали на 64 бита сразу, каждый бит подсчитывал намного больше, поэтому, подписав vs беззнаковые байты или шорты, на самом деле значило намного чаще и, очевидно, было правильным решением проекта, Сегодня просто использование подписанного int более чем достаточно для почти всех случаев обычного программирования, и если вашей программе действительно нужно использовать значения, превышающие 2 ^ 31 - 1, вы часто просто хотите долгое время. Как только вы попадаете на территорию использования длин, вам еще труднее придумать причину, по которой вы действительно не можете справиться с 2 ^ 63 - 1 положительными целыми числами. Всякий раз, когда мы переходим на 128-битные процессоры, это будет еще меньше.

Ответ 12

Ваш вопрос: "Почему Java не поддерживает unsigned ints?"

И мой ответ на ваш вопрос заключается в том, что Java хочет, чтобы все его примитивные типы: байт, char, короткий, int и long следует рассматривать как байт, слово, dword и qword соответственно, точно так же, как в сборке, а операторы Java - это подписанные операции для всех его примитивных типов, кроме char, но только на char, они имеют значение без знака 16 бит.

Таким образом, статические методы предполагают неподписанные операции также для 32 и 64 бит.

Вам нужен конечный класс, статические методы которого можно вызвать для операций без знака.

Вы можете создать этот последний класс, назвать его любым именем и реализовать его статические методы.

Если вы не знаете, как реализовать статические методы, то эта может вам помочь.

На мой взгляд, Java не похожа на С++ вообще, если ни не поддерживает беззнаковые типы и перегрузка оператора, поэтому я считаю, что Java следует рассматривать как совершенно другой язык как из С++, так и из C.

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

Поэтому я не рекомендую в Java набирать код, похожий на C, и я не рекомендую набирать код, похожий на С++, потому что тогда в Java вы не сможете делать то, что хотите делать дальше в С++, т.е. код не будет продолжать быть С++, как вообще, и для меня это плохо, чтобы сделать такой код, чтобы изменить стиль в середине.

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

Также я рекомендую не использовать короткие, int и длинные примитивные типы и использовать word, dword и qword, и вы хотите вызвать статические методы для неподписанных операций и/или подписанных операций вместо использования операторов.

Если вы собираетесь выполнять только подписанные операции и использовать операторы только в коде, тогда это нормально использовать эти примитивные типы short, int и долго.

На самом деле слово, dword и qword делают не на языке, но вы можете создавать новый класс для каждого и реализация каждого из них должны быть очень легкими:

Класс word содержит только примитивный тип short, класс dword содержит только примитивный тип int и класс qword содержит только примитивный тип long. Теперь все неподписанные и подписанные методы статичны или нет, как ваш выбор, вы можете реализовать в каждом классе, то есть все 16-битные операции, как неподписанные, так и подписанные, давая имена значений в классе word, все 32-разрядные операции без знака и подписываются с помощью присваивания значений в классе dword, а все 64-битные операции без знака и подписываются, присваивая имена значений классу qword.

Если вам не нравится давать слишком много разных имен для каждого метода, вы всегда можете использовать перегрузку в Java, хорошо, чтобы читать, что Java действительно не удаляет это тоже!

Если вам нужны методы, а не операторы для 8-разрядных подписанных операций и методов для 8-разрядных неподписанных операций, которые вообще не имеют операторов, вы можете создать класс Байт (обратите внимание, что первая буква " B 'является капиталом, поэтому это не примитивный тип byte) и реализовать методы в этом классе.

О передаче по значению и передаче по ссылке:

Если я не ошибаюсь, как в С#, примитивные объекты передаются по значению естественно, но объекты класса передаются по ссылке естественным образом, поэтому это означает, что объекты типа Байт, word, dword и qwordбудет передаваться по ссылке, а не по значению по умолчанию. Я хочу, чтобы Java имел объекты struct, поскольку С# имеет, поэтому все Байт, слово, dword и qword могут быть реализованы как struct вместо класса, поэтому по умолчанию они были переданы по значению, а не по ссылке по умолчанию, как и любой объект структуры в С#, как и примитивные типы, передаются по значению, а не по ссылке по умолчанию, но потому что эта Java хуже, чем С#, и нам приходится иметь дело с этим, тогда есть только классы и интерфейсы, которые по умолчанию передаются по ссылке, а не по значению. Поэтому, если вы хотите передать Байт, слово, dword и qword по значению, а не по ссылке, например любой другой объект класса в Java, а также в С#, вам придется просто использовать конструктор копирования и его.

Это единственное решение, о котором я могу думать. Я просто хочу, чтобы я просто набрал примитивные типы в word, dword и qword, но Java не поддерживает typedef и не использует вообще, в отличие от С#, который поддерживает , используя, что эквивалентно C typedef.

О выходе:

Для той же последовательности бит вы можете печатать их разными способами: как двоичный, как десятичный (например, значение% u в C printf), как восьмеричный (например, значение% o в C printf), как шестнадцатеричный (например, значение% x в C printf) и как целое число (например, значение% d в C printf).

Обратите внимание, что C printf не знает тип переменных, передаваемых как параметры для функции, поэтому printf знает тип каждой переменной только из объекта char *, переданного первому параметру функции.

Итак, в каждом из классов: Байт, слово, dword и qword, вы можете реализовать печать метод и получить функциональность printf, даже если примитивный тип класса подписан, вы все равно можете печатать его как unsigned, следуя некоторому алгоритму, включающему операции логического и сдвига, чтобы получить цифры для вывода на выход.

К сожалению, ссылка, которую я вам предоставил, не показывает, как реализовать эти методы печати, но я уверен, что вы можете использовать Google для алгоритмов, необходимых для реализации этих методов печати. ​​

Что я могу ответить на ваш вопрос и предложить вам.

Ответ 13

Потому что тип unsigned - это чистое зло.

Тот факт, что в C unsigned - int дает unsigned, еще более злобный.

Ниже приведен снимок проблемы, которая сжигала меня более одного раза:

// We have odd positive number of rays, 
// consecutive ones at angle delta from each other.
assert( rays.size() > 0 && rays.size() % 2 == 1 );

// Get a set of ray at delta angle between them.
for( size_t n = 0; n < rays.size(); ++n )
{
    // Compute the angle between nth ray and the middle one.
    // The index of the middle one is (rays.size() - 1) / 2,
    // the rays are evenly spaced at angle delta, therefore
    // the magnitude of the angle between nth ray and the 
    // middle one is: 
    double angle = delta * fabs( n - (rays.size() - 1) / 2 ); 

    // Do something else ...
}

Вы еще заметили ошибку? Признаюсь, я только видел его после входа в отладчик.

Поскольку n имеет неподписанный тип size_t, все выражение n - (rays.size() - 1) / 2 оценивается как unsigned. Это выражение предназначено для подписанной позиции n -го луча от среднего: 1-й луч от среднего на левой стороне будет иметь положение -1, 1-е место справа будет иметь положение +1, и т.д. После принятия значения abs и умножения на угол delta я получал бы угол между n th лучом и средним.

К сожалению, для меня указанное выражение содержало злой неподписанный и вместо оценки, скажем, -1, оно оценивалось как 2 ^ 32-1. Последующее преобразование в double запечатало ошибку.

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

Ответ 14

В спецификации "C" есть несколько драгоценных камней, которые Java отбросила по прагматическим причинам, но которые медленно возвращаются к спросу разработчиков (замыкания и т.д.).

Я упоминаю первый, потому что это связано с этим обсуждением; соответствие значений указателя целочисленной арифметике без знака. И, что касается этой темы, трудность поддержания семантики без знака в мире Java со знаком.

Я бы предположил, что если бы кто-то получил альтернативное эго Денниса Ритчи, чтобы посоветовать команде разработчиков Гослинга, он предложил бы дать Signed "ноль на бесконечности", чтобы все запросы смещения адресов сначала добавляли свой АЛГЕБРАИЧЕСКИЙ РАЗМЕР КОЛЬЦА, чтобы избежать отрицательных значений.

Таким образом, любое смещение, брошенное в массив, никогда не может генерировать SEGFAULT. Например, в инкапсулированном классе, который я называю RingArray типа double, для которого необходимо поведение без знака - в контексте "вращающегося цикла":

// ...
// Housekeeping state variable
long entrycount;     // A sequence number
int cycle;           // Number of loops cycled
int size;            // Active size of the array because size<modulus during cycle 0
int modulus;         // Maximal size of the array

// Ring state variables
private int head;   // The 'head' of the Ring
private int tail;   // The ring iterator 'cursor'
// tail may get the current cursor position
// and head gets the old tail value
// there are other semantic variations possible

// The Array state variable
double [] darray;    // The array of doubles

// somewhere in constructor
public RingArray(int modulus) {
    super();
    this.modulus = modulus;
    tail =  head =  cycle = 0;
    darray = new double[modulus];
// ...
}
// ...
double getElementAt(int offset){
    return darray[(tail+modulus+offset%modulus)%modulus];
}
//  remember, the above is treating steady-state where size==modulus
// ...

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

NB. Внешний модуль%% отменяет ссылки на законные запросы, тогда как внутренний модуль% маскирует явную злобу от негативов, более негативных, чем -modulus. Если бы это когда-либо появилось в Java +.. +9 || 8+.. + spec, тогда проблема действительно превратилась бы в "программиста, который не может" самостоятельно вращать "FAULT".

Я уверен, что так называемый "дефицит" в Java unsigned int может быть восполнен с помощью одной строки.

PS: просто для того, чтобы дать контекст вышеприведенному ведению хозяйства RingArray, здесь операция-кандидат 'set' соответствует операции вышеупомянутого элемента 'get':

void addElement(long entrycount,double value){ // to be called only by the keeper of entrycount
    this.entrycount= entrycount;
    cycle = (int)entrycount/modulus;
    if(cycle==0){                       // start-up is when the ring is being populated the first time around
        size = (int)entrycount;         // during start-up, size is less than modulus so use modulo size arithmetic
        tail = (int)entrycount%size;    //  during start-up
    }
    else {
        size = modulus;
        head = tail;
        tail = (int)entrycount%modulus; //  after start-up
    }
    darray[head] = value;               //  always overwrite old tail
}

Ответ 15

Я могу вспомнить один неудачный побочный эффект. В java встроенных базах данных количество идентификаторов, которые вы можете иметь с 32-битным полем id, составляет 2 ^ 31, а не 2 ^ 32 (~ 2 миллиарда, а не ~ 4 миллиарда).

Ответ 16

Причина ИМХО заключается в том, что они/были слишком ленивы, чтобы реализовать/исправить эту ошибку. Предложив, чтобы программисты на C/С++ не понимали неподписанные, структуру, объединение, бит-бит... Просто абсурдно.

Эфир вы разговаривали с основным программистом / bash/java на грани начала программирования a la C, без какого-либо реального знания этого языка, или вы просто говорите из своего собственного разума.;)

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

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

DC