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

374

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

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

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

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

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

dsimcha
источник
137
Я не знаю, но это чертовски раздражает меня; например, гораздо сложнее написать сетевой код таким способом.
Тамас Чинеге
20
Хотелось бы, чтобы в языке / базе данных / ... мире было только два типа: число и строка :)
Ляо
5
Написание сетевого кода совсем не сложнее. BTW InputStream.read (), возвращает, например, неподписанный байт, а не подписанный, так что пример сети - путаница ИМХО. Единственное, что сбивает с толку, так это то, что вы полагаете, что запись значения со знаком отличается от записи значения без знака. т.е. если вы на самом деле не знаете, что происходит на уровне байтов.
Питер Лори
19
@ZachSaw - я также сделал двойной дубль, когда увидел, что проектировщик языка сделал эту цитату. Нет ничего проще, чем целое число без знака. Целые числа со знаком сложны. Особенно, если учесть, что бит на вертушке находится на уровне транзистора. И как меняется целое число со знаком? Мне пришлось сделать вывод, что у разработчика Java есть серьезная проблема с пониманием логической логики.
пп.
8
Мне становится все труднее выполнять какую-либо обработку изображений с изображениями, которые byteне могут дать ровный 140уровень серого, но -116который вам необходим, & 0xffчтобы получить правильное значение.
Матье

Ответы:

193

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

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

Uri
источник
222
Я собираюсь не согласиться с Гослингом здесь с конкретным примером (от CLR не меньше). Что более запутанно, давая массиву целочисленное значение длины со знаком или длину без знака? Для массива невозможно иметь отрицательную длину, но наш API указывает, что это возможно.
JaredPar
18
Аргумент о том, чтобы сделать Java простым, является частью того, что привело нас к целой неразберихе с отсутствием шаблонов, которые они в конечном итоге принесли в язык, потому что альтернативы были настолько громоздкими. Я действительно думаю, что можно поддерживать неподписанные целые с соответствующим классом, хотя, для этого не нужны примитивы
Uri
59
Если Java нуждается в целых числах без знака, потому что индексы массива не могут быть отрицательными, тогда ей также нужны поддиапазоны (а-ля Паскаль), потому что индекс массива не может быть больше размера массива.
Уэйн Конрад
81
Хорошо, он только что рассказал о преимуществах отсутствия неподписанных типов. Теперь давайте посчитаем недостатки ...
Моше Рева
83
Я предпочитаю простоту кода над простотой языка. Вот почему я ненавижу Java.
Пиюсн
50

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

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

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

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

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

Нил Коффи
источник
2
Я также хотел бы иметь байты без знака, но я подозреваю, что преимущество полной согласованности между целочисленными типами перевешивает удобство, которое принесут байты без знака.
Алан Мур
64
«В повседневных целях они чувствовали, что наиболее распространенной потребностью были подписанные типы данных». В моем коде C ++ я часто думаю: «С какой стати я здесь использую целое число со знаком вместо беззнакового ?!». У меня такое ощущение, что «подписанный» является скорее исключением, чем правилом (конечно, это зависит от домена, но есть причина, по которой положительные целые числа называются натуральными числами ;-)).
Люк Турай
15
большой палец для вызова неподписанных байтов, при обработке изображений, предполагая, что байты не подписаны (как и должно быть), я потратил часы на отладку.
Хелин Ван
7
вы бы удивились, как часто shortэто используется - алгоритмы defltate / gzip / inflate являются 16-битными, и они сильно зависят от коротких замыканий ... или, по крайней мере, short[][по общему признанию, они являются нативными - хотя java-имплантат алгоритма несет террабайты данных]. Последний ( short[]) имеет значительное преимущество, int[]поскольку он занимает вдвое меньше памяти и меньше памяти = лучшие свойства кэширования, намного лучшая производительность.
bestsss
8
Хотя в конкретном приложении вы должны измерить , дает ли шорты более высокую производительность, чем предполагать, что это правда. Возможно, что дополнительные jiggery-pokery, требуемые для манипулирования шортами, а не целыми числами (который обычно является типом, который процессор «любит использовать»), могут фактически нанести ущерб производительности в конкретном приложении. Не всегда, но вы должны проверить, а не предполагать.
Нил Коффи
19

Это более старый вопрос, и Пэт кратко упомянул 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целых чисел.

Jyro117
источник
2
Но, тем не менее, charон слишком мал, чтобы поддерживать longарифметику, например.
3
Это может быть недостатком Java
Надеемся, что они поддерживают значения без знака для байтов. Делает вещи проще.
mixedz
15

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

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

похлопывание
источник
37
Да, я уверен, что Гослинг очень не осведомлен о Java по сравнению с вами.
jakeboxer
Позволяет ли Java выполнять арифметику непосредственно для беззнаковых байтов, или значения всегда продвигаются? Имея тип без знака для хранения, но всегда выполняя арифметику с типом со знаком, который является достаточно большим, чтобы вместить его, хорошо работает семантически, но приведет к тому, что операции с типами без знака, которые имеют тот же размер, что и у «обычных» целых чисел, будут более дорогими.
суперкат
2
Это плохой стиль charдля всего, кроме персонажей.
Звездный синий
5
@starblue Конечно, но это хак, чтобы обойти ограничение языка
Basic
14

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

Бомбы
источник
12
Что касается микширования со знаком / без знака: вы можете иметь типы без знака, но запретить микширование (или требовать явного приведения). Тем не менее, не ясно, нужно ли это.
Слеск
2
В C ++ вы должны разбросать static_castмного вокруг, чтобы смешать их. Это действительно грязно.
Raedwald
4
8-й бит есть, он просто пытается скрыться как знак.
звездно-голубой
Вещи только становятся беспорядочными с типами 32 бита или больше. Я не вижу причин, по которым Java не должна была byteбыть подписана, как это было в Паскале.
суперкат
12
Приходите ко мне, когда у вас возникнут проблемы с обработкой изображений в Java, где вы ожидаете, что байты будут без знака. Тогда вы поймете, что при & 0xFFкаждом продвижении между байтами и кодом код становится еще сложнее.
bit2shift
12

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

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

akatakritos
источник
34
Целые числа со знаком Java тоже вращаются. Я не понимаю вашу точку зрения.
Foo
8
@foo: целые числа со знаком должны стать большими, прежде чем они вызовут проблемы. Напротив, в C могут возникнуть проблемы при сравнении любого отрицательного целого числа - даже -1- с любым беззнаковым числом - даже с нулем.
суперкат
Жаль, что Java не могла включать неподписанные типы, но с ограниченным набором преобразований и смешанных операторов (несколько аналогично тому, как в C можно добавить 5 к указателю, но нельзя сравнить указатель с 5) , Идея о том, что использование оператора для смешанных типов, когда существует неявное приведение, должна вызывать неявное использование этого преобразования (и использование последующего типа в качестве типа результата) лежит в основе многих сомнительных проектных решений как в .NET, так и в. Ява.
Суперкат
4
Не ржать на ваш ответ, но иметь -1«неизвестный» возраст (как предполагает статья) - один из классических примеров «запаха кода» . Например, если вы хотите вычислить «насколько Алиса старше Боба?», А A = 25 и B = -1, вы получите ответ, ±26который просто неверен. Правильная обработка неизвестных значений является своим родом , Option<TArg>когда Some(25) - Noneвернемся бы None.
bytebuster
11

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

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

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

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

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

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

starblue
источник
5
Да, для перехода вправо вы должны выбрать между >>и >>>для подписанного и без знака, соответственно. Сдвиг влево не проблема.
звездный синий
1
@starblue На самом деле >>>не работает для shortи byte. Например, (byte)0xff>>>1урожайность, 0x7fffffffа не 0x7f. Другой пример: byte b=(byte)0xff; b>>>=1;приведет к b==(byte)0xff. Конечно, вы можете сделать, b=(byte)(b & 0xff >> 1);но это добавляет еще одну операцию (поразрядно &).
CITBL
7
«... Даже с упрощенной моделью большинство Java-программистов не знают, как ведут себя базовые числовые типы ...» Что-то во мне просто обижается на язык, нацеленный на наименьший общий знаменатель.
Основное
Открытие линии в своем ответе, о дополнительной сложности и мало выгоды, именно то , что я подробно в моей статье 6 лет спустя: nayuki.io/page/unsigned-int-considered-harmful-for-java
Nayuki
1
@Nayuki Ваша статья действительно хорошая. Только небольшое замечание, я бы использовал добавление 0x80000000 для операторов сравнения вместо XOR, потому что оно объясняет, почему это работает, оно сдвигает непрерывную область, в которой происходит сравнение, с -MAXINT на 0. Побитовый эффект его точно такой же.
звездно-голубой
6

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

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

Джон Хэскол
источник
12
aka «Так что люди действительно используют его, и мы ошибались, если не включали его для начала», - но мы все еще не совсем доверяем разработчикам Java, чтобы узнать, подписана ли переменная или нет - поэтому мы не собираемся их реализовывать в ВМ или как типы, эквивалентные их подписанным двоюродным братьям.
Основное
6

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

Мортеза Ади
источник
4

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

Роб Оттауэй
источник
Это было бы хорошо ... за исключением того, что свидетельство из интервью Гослинга подразумевает, что целые числа без знака (кроме char) были опущены, потому что дизайнеры думали, что они были плохой идеей ... учитывая цели языка.
Стивен С.
Хорошей идеей является никогда не придавать слишком большого значения показаниям свидетелей, если есть документальные свидетельства.
user7610
4

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

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

Джонатан
источник
2

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

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

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

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

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

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

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

Между прочим, в названии языков оно также совершенно иное.

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

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

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

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

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

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

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

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

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

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

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

О выходе:

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

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

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

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

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


источник
MASM (ассемблер Microsoft) и Windows определяют BYTE, WORD, DWORD, QWORD как неподписанные типы. Для MASM SBYTE, SWORD, SDWORD, SQWORD являются подписанными типами.
rcgldr
1

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

Тот факт, что в Си 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 и т.д. После взяв значение abs и умножив на deltaугол, я получу угол между nсредним и третьим лучом.

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

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

Майкл
источник
Добавление «unsigned long» в Java было бы неудобно. Однако добавление меньших типов без знака не должно вызывать проблем. Особенно типы, меньшие, чем «int», могли бы быть легко обработаны, если бы их повысили до «int» численно очевидным образом, а «unsigned int» можно было бы обработать, сказав, что операции, включающие в себя подписанные int и неподписанные int, будут способствовать оба операнда «длинные». Единственная проблемная ситуация - это операции с длинным без знака и количеством со знаком, так как не было бы типа, способного представлять все значения обоих операндов.
суперкат 26.09.16
@supercat: если при каждой операции unsignedконвертируется intв чем польза unsigned? Он не будет иметь никакой функциональности, отличимой от short. И если вы преобразуете intтолько в смешанные операции, такие как unsigned+intили unsigned+float, то у вас все еще остается проблема ((unsigned)25-(unsigned)30)*1.0 > 0, которая является основной причиной unsignedошибок, связанных с.
Майкл
Многие операции с неподписанными типами будут продвигаться на «long». Требование явного приведения при сохранении результата обратно к неподписанным типам вызовет те же самые неудобства, которые существуют с коротким и байтовым типами, но если тип является в основном форматом хранения, а не форматом вычисления, который не должен быть проблемой. В любом случае неподписанные типы, короче, чем «int», должны просто быть в состоянии перейти к «int» без затруднений.
суперкат 26.09.16
3
Мне не нравится этот ответ, потому что он использует аргумент "целые числа без знака злы и не должны существовать, потому что они никогда не могут быть подписаны". Любой, кто пытается вычесть целое число без знака, должен это уже знать. А что касается читабельности, С не совсем известен тем, что за ним легко следить. Кроме того, (полу) аргумент «лишний бит не стоит лишних хлопот» также очень слаб. Обработка ошибок вместо того, чтобы exit(1);действительно «стоить дополнительных проблем»? Разве невозможность открывать большие файлы действительно стоит безопасности, которую не испортят менее опытные Java-программисты unsigned?
yyny
2
Единственное зло, которое я вижу в этом коде, это n - (rays.size() - 1) / 2. Вы должны всегда заключать в скобки бинарные операторы, потому что читатель кода не должен предполагать что-либо о порядке операций в компьютерной программе. То, что мы обычно говорим, что a + b c = a + (b c), не означает, что вы можете предположить это при чтении кода. Кроме того, вычисление должно быть определено вне цикла, чтобы его можно было проверить без наличия цикла. Это ошибка в том, что ваши типы не совпадают, а не в целых числах без знака. В C вы должны убедиться, что ваши типы выстраиваются в линию.
Дмитрий
0

В спецификации «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 никогда не получит отрицательный индекс, даже если вредоносный запросчик попытается это сделать. Помните, что существует также много законных запросов для запроса предыдущих (отрицательных) значений индекса.

Примечание: внешний модуль% отменяет ссылки на законные запросы, тогда как внутренний модуль% маскирует явную злобу от негативов, более негативных, чем -модуль. Если бы это когда-либо появилось в 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
}
MKhomo
источник
-2

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

Майк Г
источник
1
Вероятно, он думает о массивах и не может использовать отрицательные целые числа в качестве индексов. Вероятно.
SK9
2
Когда автоинкрементные поля в базах данных переполняются, они часто выходят из строя.
Джошуа
-8

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

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

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

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

ОКРУГ КОЛУМБИЯ

Денис Ко
источник
34
Просто для удовольствия, Google фраза "сам вращающийся цикл". Очевидно , что Денис Ко - единственный человек в мире, достойный называть себя программистом :-)
Стивен С.
6
Этот ответ настолько плох, что это смешно
Наюки