Чому Java не підтримує неподписані вставки?


374

Чому Java не включає підтримку непідписаних цілих чисел?

Мені це здається дивним упущенням, враховуючи те, що вони дозволяють писати код, який менш імовірно створює переповнення на несподівано великий вхід.

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

Нарешті, в деяких випадках непідписані цілі числа можуть бути ефективнішими для певних операцій, таких як поділ.

Який мінус включати їх?


137
Я не знаю, але це мене дратує пекло; наприклад, набагато складніше написати мережевий код таким чином.
Тамас Цінеге

20
Я хотів би, щоб у мові / базі даних / ... було всього два типи: число та рядок :)
Ліао

5
Написати мережевий код зовсім не складніше. BTW InputStream.read (), повертає неподписаний байт, а не підписаний, наприклад, наприклад, мережевий приклад - це замішання IMHO. Єдина заплутана ситуація - ви припускаєте, що написання підписаного значення відрізняється від написання неподписаного. тобто якщо ви насправді не знаєте, що відбувається на рівні байтів.
Пітер Лодрі

19
@ZachSaw - я також зробив подвійний результат, коли побачив, що мовна дизайнерка робить цю цитату. Немає нічого простішого за непідписане ціле число. Цілі числа, що підписалися, є складними. Особливо, коли ви розглядаєте подвійне скручування на транзисторному рівні. І як змінюється підписане ціле число? Я повинен був зробити висновок, що у дизайнера Java є серйозне питання розуміння булевої логіки.
ПП.

8
Мені стає важче виконати будь-яку обробку зображень із зображеннями, byteне в змозі надати рівень прямого 140сірого, але такий, -116який вам потрібно отримати, & 0xffщоб отримати правильне значення.
Матьє

Відповіді:


193

Це з інтерв'ю з Гослінг та іншими , про простоту:

Гослінг: Для мене як мовного дизайнера, який я насправді не вважаю таким, як сьогодні, я міг би сподіватися на те, що "простий" сенс закінчився. Це визначення говорить, що, наприклад, Java не є - і насправді багато цих мов закінчуються безліччю кутових справ, речей, які ніхто насправді не розуміє. Випробуйте будь-якого розробника C щодо неподписаного, і досить скоро ви дізнаєтесь, що майже жоден розробник C насправді не розуміє, що відбувається з непідписаними, що таке арифметика без підпису. Такі речі зробили C складним. Мовна частина Java - я думаю, досить проста. Бібліотеки, які ви повинні шукати.


222
Мені доведеться не погодитися з Гослінг тут на конкретному прикладі (від CLR не менше). Що ще більш заплутано в наданні масиву підписаного цілого значення довжини або неподписаної довжини? Неможливо, щоб масив мав негативну довжину, проте наш API вказує, що це можливо.
JaredPar

18
Аргумент того, щоб зробити Java простою, є частиною того, що змусило нас у цілому безладі з відсутністю шаблонів, які вони врешті-решт ввели в мову, оскільки альтернативи були настільки громіздкими. Я думаю, що можна підтримати неподписані вставки з відповідним класом, хоча йому не потрібні праймери
Uri

59
Якщо Java потрібні цілі цілі без підпису, оскільки індекси масиву не можуть бути негативними, то йому також потрібні піддиаграми (a la Pascal), оскільки індекс масиву не може бути більшим за розмір масиву.
Уейн Конрад

81
Гаразд, він просто сказав про переваги відсутності неподписаних типів. Тепер давайте порахуємо недоліки ...
Моше Рева

83
Я віддаю перевагу простоті коду над простотою мови. Тому я ненавиджу Java.
Pijusn

50

Читаючи між рядків, я думаю, що логіка була приблизно такою:

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

Здебільшого, я б сказав, це було розумне рішення. Можливо, я мав би:

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

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


2
Я також хотів би мати неподписані байти, але я підозрюю, що перевага повної послідовності між цілими типами переважає зручність, яку принесуть непідписані байти.
Алан Мур

64
"Для повсякденних цілей вони вважали, що найчастіше потрібні підписані типи даних". У своєму коді C ++ я більше, ніж часто, думаю, що "Чому на землі я використовую тут підписане ціле число замість неподписаного ?!". У мене таке відчуття, що "підписаний" - це виняток, а не правило (звичайно, це залежить від домену, але є причина, чому додатні цілі числа називаються натуральними числами ;-)).
Люк Турей

15
великий палець вгору для виклику непідписаних байтів, під час обробки зображень, припускаючи, що байти не підписані (як і належить), змусив мене витрачати години на налагодження.
Хелін Ван

7
ви здивувались тому, як часто shortвикористовується алгоритми defltate / gzip / inflate - 16-бітні, і вони сильно покладаються на шорти ... або, принаймні, short[][правда, вони є рідними - все-таки java impl алгоритму переносить терабайти даних]. Останнє ( short[]) має значну перевагу, int[]оскільки займає вдвічі менше пам’яті та менше пам’яті = кращі властивості кешування, значно краща продуктивність.
bestsss

8
Хоча в конкретному додатку ви повинні виміряти, чи використання шортів дає вам кращу ефективність, а не вважати, що це правда. Можливо, що додаткові джиггери-покери, необхідні для маніпулювання шортами, а не ints (що зазвичай є типом, який процесор «любить використовувати»), насправді можуть завдати шкоди для продуктивності в певній програмі. Не завжди, але слід перевірити, а не припускати.
Ніл Коффі

19

Це давнє запитання, і Пат коротко згадував про чар, я просто думав, що слід розширити це для інших, хто буде дивитись на це вниз. Давайте детальніше розглянемо примітивні типи 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цілих чисел.


2
Але, наприклад char, занадто мало, щоб підтримувати longарифметику, наприклад.

3
Це може бути недоліком Java

Сподіваючись, що вони підтримують непідписані значення для байтів. Полегшує роботу.
суміш

15

У Java є неподписані типи або хоча б один: char - це непідписаний шорт. Тож будь-яке виправдання, яке підкидає Гослінг, це насправді лише його незнання, чому немає інших неподписаних типів.

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


37
Так, я впевнений, що Гослінг дуже невідомий щодо Java в порівнянні з вами.
Джейкбоксер

Чи дозволяє Java виконувати арифметику безпосередньо на безпідписаних величинах, або значення завжди отримують? Маючи неподписаний тип для зберігання, але завжди виконуючи арифметику на підписаному типі, який є достатньо великим, щоб вмістити його, працює добре семантично, але може призвести до того, що операції з неподписаними типами такого ж розміру, як "звичайні" цілі числа, будуть дорожчими.
supercat

2
Це поганий стиль використовувати charдля чого-небудь, крім персонажів.
starblue

5
@starblue Звичайно, є, але це хак , щоб обійти обмеження мови
Basic

14

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


12
Щодо змішування з підписаним / непідписаним: Ви можете мати неподписані типи, але забороняти змішування (або вимагати явних кастингу). Все-таки не ясно, чи потрібно.
sleske

2
У C ++ вам потрібно static_castсильно посипати s, щоб їх змішати. Це справді безладно.
Raedwald

4
Восьмий біт є, він просто намагається приховати себе як знак.
starblue

Речі стають безладними лише з типом 32 біт або більше. Я не бачу причин, чому Java не повинна була byteбути підписана так, як це було в Pascal.
supercat

12
Заходьте до мене, коли у вас виникають проблеми з обробкою зображень на Java, де ви очікуєте, що байти не підписуються. Тоді ви дізнаєтесь, що & 0xFFкожне просування байта до інту робить код ще більш м'яким.
bit2shift

12

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

Цей хлопець каже, тому що стандарт C визначає операції, що включають безпідписані та підписані входи, які слід трактувати як неподписані. Це може призвести до негативного підписання цілих чисел у великий неподписаний int, що може призвести до помилок.


34
Цілі числа, підписані Java, теж обертаються. Я не бачу вашої суті.
foo

8
@foo: Підписані цілі числа повинні бути великими, перш ніж вони стануть проблемами. Навпаки, у C можуть виникнути проблеми зі порівнянням будь-якого від’ємного цілого числа - навіть -1- з будь-якою безпідписаною кількістю - навіть нуля.
supercat

Дуже погано Java не могла включати неподписані типи, але з обмеженим набором конверсій та змішаними операторами (дещо аналогічно тому, що в C можна додати 5 до вказівника, але не можна порівняти покажчик на 5) . Ідея про те, що використання оператора на змішаних типах, коли існує неявна передача, має примусити неявне використання цього складу (і використовувати послідовний тип як тип результату) лежить в основі багатьох сумнівних дизайнерських рішень як в .NET, так і в Java.
supercat

4
Не лунати на свою відповідь, але мати -1«невідомий» вік (як підказує стаття) - один із класичних прикладів «кодового запаху» . Наприклад, якщо ви хочете обчислити "на скільки Аліса старша за Боб?", І A = 25 і B = -1, ви отримаєте відповідь±26 яка просто неправильна. Правильна обробка невідомих значень є своїм родом , Option<TArg>коли Some(25) - Noneповернемося б None.
bytebuster

11

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

Що стосується практичних порад:

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

  • Використовуйте менші типи лише для масивів, коли вам потрібно заощадити місце.

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

Дивись також цю відповідь про "перенесення генератора випадкових чисел з C на Java".


5
Так, для зрушення праворуч вам потрібно вибрати відповідно >>і >>>для підписаних, і без підписів. Зсув вліво - це не проблема.
starblue

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. Побітовий ефект точно такий же.
starblue

6

З JDK8 він має деяку підтримку для них.

Ми все ще можемо побачити повну підтримку неподписаних типів на Java, незважаючи на занепокоєння Gosling.


12
aka "Таким чином, люди дійсно користуються ним, і ми помилилися, що не включили його для початку", - але ми все ще не дуже довіряємо Java-розробникам, щоб знати, чи підписана змінна чи ні - тому ми не збираємось їх реалізовувати у ВМ або як типи, еквівалентні їхнім підписаним родичам.
Основні

6

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


4

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


Це було б добре ... окрім доказів інтерв'ю з Гослінг випливає, що безпідписані цілі числа (крім char) були опущені через те, що дизайнери вважали, що це погана ідея ... враховуючи цілі мови.
Стівен С

Ідеї ​​ніколи не вкладають надто великого значення у висловлюваннях очевидців, якщо документальні свідчення також є під рукою.
користувач7610

4

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

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


2

Ваше питання "Чому Java не підтримує неподписані вставки"?

І моя відповідь на ваше запитання полягає в тому, що Java хоче, щоб усі її примітивні типи: байт , char , короткий , int і long слід розглядати як байт , слово , dword і qword відповідно, точно так само, як у зборі, і оператори Java підписуються Операції з усіма його примітивними типами, крім char , але тільки на char вони не підписані лише 16 біт.

Таким чином, статичні методи можуть вважати операції без підпису і для 32, і для 64 біт.

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

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

Якщо ви не маєте уявлення про те, як реалізувати статичні методи, тоді це посилання може вам допомогти.

На мій погляд, Java це НЕ схоже на C ++ взагалі , якщо він ні не підтримує беззнакові типи , ні перевантаження операторів, тому я думаю , що Java слід розглядати як абсолютно іншу мову як від C ++ і від C.

Це, до речі, також зовсім інше в назві мов.

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

Я рекомендую писати та використовувати статичні методи також для підписаних операцій, так що ви не бачите в кодовій суміші операторів і статичних методів як для підписаних, так і для непідписаних операцій, якщо вам не потрібні лише підписані операції в коді, і це добре використовувати лише операторів.

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

Якщо ви збираєтеся робити лише підписані операції та використовувати оператори лише в коді, тоді це нормально, щоб використовувати ці примітивні типи короткий , int та long .

На насправді слово , DWord і QWORD робити НЕ існує в мові, але ви можете створити новий клас для всіх і реалізація кожного має бути дуже легко:

Клас слово має примітивний тип короткий тільки клас DWORD має примітивний тип Int тільки і клас QWORD тримає примітивний тип довго тільки. Тепер усі непідписані та підписані методи як статичні чи не як ваш вибір, ви можете реалізувати у кожному класі, тобто всі 16-бітові операції як без підпису, так і підписані, надаючи значущі імена для класу word , всі 32-бітні операції як без підпису, так і підписується шляхом додавання значень імен класу dword та всіх 64-бітових операцій як без підпису, так і підписується шляхом надання значень імен класу qword .

Якщо вам не подобається давати занадто багато різних імен для кожного методу, ви завжди можете використовувати перевантаження в Java, добре читати, що Java теж не видаляла цього!

Якщо ви хочете скористатися методами, а не операторами для 8-бітових підписаних операцій та методів для 8-бітових безпідписаних операцій, у яких взагалі немає операторів, тоді ви можете створити клас Byte (зауважте, що перша літера "B" є великою літерою, тому це не примітивний тип байта ) та реалізувати методи цього класу.

Про проходження за значенням та проходження за посиланням:

Якщо я не помиляюся, як, наприклад, у C #, примітивні об'єкти передаються за значенням природним чином, але об'єкти класу передаються посиланням природним чином, так що це означає, що об'єкти типу Byte , word , dword та qword передаватимуться посиланням, а не значенням за замовчуванням. Я хочу Java мала STRUCT об'єктів в C # є, так що все Byte , слово , подвійне слово і QWORD можуть бути реалізовані як структура замість класу, тому за замовчуванням вони передавались за значенням, а не за посиланням за замовчуванням, як і будь-який структурний об'єкт у C #, як і примітивні типи, передаються за значенням, а не за посиланням за замовчуванням, а тому, що Java гірша за C # і ми маємо щоб вирішити це, тоді є лише класи та інтерфейси, які передаються посиланням, а не значенням за замовчуванням. Отже, якщо ви хочете передати об'єкти байт , слово , dword та qword за значенням, а не за посиланням, як і будь-який інший об’єкт класу на Java, а також на C #, вам доведеться просто використовувати конструктор копій, і все.

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

Про вихід:

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

Зауважте, що C printf не знає тип змінних, що передаються як параметри функції, тому printf знає тип кожної змінної лише з об'єкта char *, переданого першому параметру функції.

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

На жаль, посилання, яке я вам дав, не показує, як реалізувати ці методи друку, але я впевнений, що ви можете шукати в Google за алгоритмами, необхідними для реалізації цих методів друку.

Це все, що я можу відповісти на ваше запитання та запропонувати вам.


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, 1-й з правого - позицію +1 тощо. Після приймаючи значення abs і помножуючи на deltaкут, я отримав би кут між nпроменем та середнім.

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

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


Додавання "довгих неподписаних" до Java було б незручно. Додавання менших неподписаних типів, однак, не повинно створювати проблем. Особливо типи, менші за "int", можна було б легко обробити, якщо вони сприяють "int" численно-очевидно, і "unsigned int" можна було б обробляти, кажучи, що операції, що включають підписаний int та непідписаний int, сприятимуть обидва операнди до "довгих". Єдиною проблемною ситуацією були б операції, що включають непідписане довге і підписане кількість, оскільки не було б типу, здатного представляти всі значення обох операндів.
supercat

@supercat: якщо unsignedперетворюється intна кожну операцію, в чому користь unsigned? Він не матиме жодних функціональних можливостей short. І якщо ви переходите intлише на змішані операції, наприклад, unsigned+intабо unsigned+float, то у вас все ще виникає проблема ((unsigned)25-(unsigned)30)*1.0 > 0, яка є основною причиною unsignedпомилок, пов'язаних з цим.
Майкл

Багато операцій на неподписаних типах сприяло б "довгому". Потрібні явні касти під час зберігання результату до неподписаних типів можуть спричинити майже ті ж роздратування, що й у коротких та байтових, але якщо тип переважно формат зберігання, а не формат обчислень, це не повинно бути проблемою. У будь-якому випадку, неподписані типи, коротші за "int", повинні бути спроможними без особливих труднощів перейти до "int".
supercat

3
Мені не подобається ця відповідь, оскільки вона використовує аргумент "непідписані цілі числа є злими і не повинні існувати, тому що вони ніколи не можуть бути підписані". Той, хто намагається відняти ціле число, яке не має значення, повинен це знати вже. Що стосується читабельності, то C не точно відомий тим, що його легко слідкувати. Крім того, (напів-) аргумент "зайвий біт не вартий зайвих клопотів" також дуже слабкий. Чи поводження з помилками замість того, щоб exit(1);насправді «вартувати зайвих клопотів»? Невже не в змозі відкрити великі файли дійсно варто безпеки, яку менш досвідчені програмісти Java не зіпсують unsigned?
yyny

2
Єдине зло, що я бачу в цьому коді, - це n - (rays.size() - 1) / 2. Ви завжди повинні сковувати бінарні оператори, тому що читачеві коду не потрібно припускати нічого про порядок операцій у комп'ютерній програмі. Тільки тому, що ми умовно кажемо, що + b c = a + (b c) не означає, що ви можете припустити це при читанні коду. Крім того, слід визначити обчислення поза циклом, щоб його можна було перевірити без присутності циклу. Це помилка в тому, щоб не переконатися, що ваші типи розташовуються, а не проблема непідписаних цілих чисел. На мові C вам належить переконатися, що ваші типи вишиковуються.
Дмитро

0

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

Я згадую перший, тому що він пов'язаний з цією дискусією; прихильність значень вказівника до непідписаної цілочисельної арифметики. І стосовно цієї теми теми, складність у підтримці непідписаної семантики у світі підписів Java.

Я б здогадувався, якби отримати Денніс Річі альтер-его, щоб порадити команду дизайнерів Gosling, він запропонував би дати Signed "нуль у нескінченності", щоб усі запити на зміщення адреси спочатку додавали свій РАЗМЕР ALGEBRAIC RING для усунення негативних значень.

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

// ...
// 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 + .. + специфікація, тоді проблема справді перетвориться на "програміста, який не може" самостійно обертатись "НЕПРАВНО".

Я впевнений, що так званий Java непідписаний int 'дефіцит' може бути компенсований за допомогою вищевказаного одного вкладиша.

PS: Просто, щоб дати контекст вищевказаному господарству RingArray, ось операція "встановити" кандидата, щоб відповідати вищевказаній операції "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
}

-2

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


1
Він, ймовірно, думає про масиви і не може використовувати негативні цілі числа в якості індексів. Мабуть.
SK9

2
Коли поля з автоматичним збільшенням в базах даних переповнюються, вони часто йдуть вако.
Джошуа

-8

Причина ІМХО полягає в тому, що вони / були ліниві, щоб реалізувати / виправити цю помилку. Припустити, що програмісти C / C ++ не розуміють безпідписаного, структури, об'єднання, бітового прапора ... Це просто безглуздо.

Ефір, з яким ви розмовляли з базовим / bash / java-програмістом на межі початку програмування a la C, не маючи реальних знань на цій мові або ви просто розмовляєте не з власного розуму. ;)

коли ти щодня розбираєшся у форматі або з файлу, або з обладнання, ти починаєш ставити під сумнів, про що вони думали?

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

Постійного струму


34
Тільки для ударів, Google словосполучення "петля, що самообертається". Ясна річ , Денис Ко - єдина людина у світі, яка гідна називати себе програмістом :-)
Стівен C

6
Ця відповідь настільки погана, що смішна
Наюкі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.