Я знаю, вопрос кажется странным. Программисты иногда слишком много думают. Пожалуйста, прочтите ...
В CI много использую signed
и unsigned
целые числа. Мне нравится, что компилятор предупреждает меня, если я делаю такие вещи, как присвоение целого числа со знаком беззнаковой переменной. Я получаю предупреждения, если сравниваю подписанные с целыми числами без знака и многое другое.
Мне нравятся эти предупреждения. Они помогают мне поддерживать правильный код.
Почему у нас нет такой же роскоши для поплавков? Квадратный корень никогда не вернет отрицательное число. Есть и другие места, где отрицательное значение с плавающей запятой не имеет значения. Идеальный кандидат для беззнакового числа с плавающей запятой.
Кстати, я не особо заинтересован в единственной дополнительной точности, которую я мог бы получить, удалив знаковый бит из поплавков. Я очень доволен float
такими, какие они есть сейчас. Я просто хотел бы иногда отмечать число с плавающей запятой как беззнаковое и получать такие же предупреждения, как и с целыми числами.
Я не знаю ни одного языка программирования, который поддерживает числа с плавающей запятой без знака.
Есть идеи, почему их не существует?
РЕДАКТИРОВАТЬ:
Я знаю, что FPU x87 не имеет инструкций по работе с беззнаковыми числами с плавающей запятой. Давайте просто воспользуемся подписанными инструкциями с плавающей запятой. Неправильное использование (например, опускание ниже нуля) может рассматриваться как неопределенное поведение точно так же, как переполнение целых чисел со знаком не определено.
Ответы:
Почему C ++ не поддерживает беззнаковые числа с плавающей запятой, заключается в том, что ЦП не выполняет эквивалентных операций машинного кода. Так что поддерживать его было бы очень неэффективно.
Если бы C ++ поддерживал это, вы бы иногда использовали беззнаковое число с плавающей запятой и не осознавали, что ваша производительность просто упала. Если бы C ++ поддерживал это, то каждую операцию с плавающей запятой нужно было бы проверять, чтобы узнать, подписана она или нет. А для программ, которые выполняют миллионы операций с плавающей запятой, это неприемлемо.
Таким образом, возникает вопрос, почему разработчики оборудования не поддерживают его. И я думаю, что ответ на этот вопрос заключается в том, что изначально не существовало стандарта беззнаковых чисел с плавающей запятой. Поскольку языки любят быть обратно совместимыми, даже если они были добавлены, языки не могли их использовать. Чтобы увидеть спецификацию с плавающей запятой, вы должны взглянуть на стандарт IEEE 754 Floating-Point .
Вы можете обойтись без беззнакового типа с плавающей запятой, создав класс с плавающей запятой без знака, который инкапсулирует float или double и выдает предупреждения, если вы пытаетесь передать отрицательное число. Это менее эффективно, но, вероятно, если вы не используете их интенсивно, вас не будет беспокоить эта небольшая потеря производительности.
Я определенно вижу полезность беззнакового числа с плавающей точкой. Но C / C ++ предпочитает безопасность, которая работает лучше всего для всех.
источник
int
, вся проверка типов, связанная со знаком, может происходить во время компиляции. OP предполагает, чтоunsigned float
это будет реализовано как обычноеfloat
с проверками во время компиляции, чтобы гарантировать, что некоторые бессмысленные операции никогда не выполняются. Полученный машинный код и производительность могут быть идентичными, независимо от того, подписаны ли ваши числа с плавающей запятой или нет.Существует значительная разница между целыми числами со знаком и без знака в C / C ++:
Значения со знаком оставляют верхний бит неизменным (расширение знака), значения без знака очищают верхний бит.
Причина отсутствия беззнакового числа с плавающей запятой заключается в том, что вы быстро столкнетесь со всевозможными проблемами, если нет отрицательных значений. Учти это:
Какое значение имеет c? -8. Но что бы это значило в системе без отрицательных чисел. FLOAT_MAX - 8 возможно? На самом деле, это не работает, поскольку FLOAT_MAX - 8 - это FLOAT_MAX из-за эффектов точности, так что все еще хуже. Что, если бы это было частью более сложного выражения:
Это не проблема для целых чисел из-за природы системы дополнения до 2.
Также рассмотрите стандартные математические функции: sin, cos и tan будут работать только для половины их входных значений, вы не можете найти журнал значений <1, вы не можете решить квадратные уравнения: x = (-b +/- root ( bb - 4.ac)) / 2.a и так далее. Фактически, это, вероятно, не сработает для какой-либо сложной функции, поскольку они, как правило, реализуются как полиномиальные приближения, которые будут где-то использовать отрицательные значения.
Итак, беззнаковые числа с плавающей точкой бесполезны.
Но это не значит, что класс, который проверяет диапазон значений с плавающей запятой, бесполезен, вы можете захотеть ограничить значения заданным диапазоном, например вычислениями RGB.
источник
2's complement
будет столь же эффективным, как не возникнет проблем с наличием чисел с плавающей запятой без знака?value >> shift for signed values leave the top bit unchanged (sign extend)
Вы уверены, что? Я думал, что это поведение определяется реализацией, по крайней мере, для отрицательных значений со знаком.0.0
или, возможно, до Inf или NaN. Или просто используйте Undefined Behavior, как OP, предложенный в редактировании вопроса. Re: функции триггера: поэтому не определяйте версии беззнакового вводаsin
и т. Д., И убедитесь, что их возвращаемое значение обрабатывается как подписанное. Вопрос не в том, чтобы заменить float на unsigned float, а просто добавить егоunsigned float
как новый тип.(Кстати, Perl 6 позволяет писать
а затем вы можете использовать так
Nonnegative::Float
же, как и любой другой тип.)Аппаратная поддержка неподписанных операций с плавающей запятой отсутствует, поэтому C не предлагает ее. C в основном предназначен для «портативной сборки», то есть максимально приближен к металлу, не привязываясь к определенной платформе.
[редактировать]
C похож на сборку: то, что вы видите, именно то, что вы получаете. Неявное выражение «Я проверю, что это значение для вас неотрицательно» противоречит его философии дизайна. Если вы действительно этого хотите, вы можете добавить что-
assert(x >= 0)
то подобное, но сделать это нужно явно.источник
of ...
не выполняет синтаксический анализ.Я считаю, что unsigned int был создан из-за необходимости большего запаса значения, чем может предложить подписанный int.
Плавающий имеет гораздо больший запас, поэтому в беззнаковом поплавке никогда не было «физической» необходимости. И, как вы указываете на себя в своем вопросе, дополнительная точность в 1 бит не за что.
Изменить: прочитав ответ Брайана Р. Бонди , я должен изменить свой ответ: он определенно прав в том, что базовые процессоры не имели операций с плавающей запятой без знака. Тем не менее, я считаю, что это было дизайнерское решение, основанное на причинах, которые я указал выше ;-)
источник
Я думаю, Треб находится на правильном пути. Для целых чисел более важно, чтобы у вас был соответствующий тип без знака. Это те, которые используются при битовом сдвиге и используются в битовых картах . Знаковый бит просто мешает. Например, при сдвиге вправо отрицательного значения результирующее значение определяется реализацией C ++. Выполнение этого с беззнаковым целым числом или его переполнение дает идеально определенную семантику, потому что на пути нет такого бита.
Так что, по крайней мере, для целых чисел необходимость в отдельном типе без знака сильнее, чем просто предупреждение. Все вышеперечисленное не нужно учитывать для поплавков. Так что, я думаю, нет реальной необходимости в аппаратной поддержке для них, и C уже не будет поддерживать их на этом этапе.
источник
C99 поддерживает комплексные числа и типичную форму sqrt, поэтому
sqrt( 1.0 * I)
будет отрицательным.Комментаторы подчеркнули небольшой блеск выше, поскольку я имел в виду
sqrt
макрос общего типа, а не функцию, и он вернет скалярное значение с плавающей запятой путем усечения комплекса до его реального компонента:Он также содержит мозговое пукание, поскольку действительная часть sqrt любого комплексного числа положительна или равна нулю, а sqrt (1.0 * I) равна sqrt (0.5) + sqrt (0.5) * I не -1.0.
источник
sqrt(-0.0)
часто производит-0.0
. Конечно, -0.0 не является отрицательным значением .Я предполагаю, что это зависит от того, что спецификации IEEE с плавающей запятой только подписаны и что большинство языков программирования используют их.
Статья в Википедии о числах с плавающей запятой IEEE-754
Изменить: Кроме того, как отмечали другие, большинство оборудования не поддерживает неотрицательные числа с плавающей запятой, поэтому обычные типы с плавающей запятой более эффективны, поскольку имеется поддержка оборудования.
источник
Я думаю, что основная причина в том, что беззнаковые числа с плавающей запятой имеют действительно ограниченное использование по сравнению с беззнаковыми целыми числами. Я не верю аргументу, что это потому, что оборудование не поддерживает это. Старые процессоры вообще не имели возможностей с плавающей запятой, все это эмулировалось программно. Если бы беззнаковые числа с плавающей запятой были полезны, они сначала были бы реализованы в программном обеспечении, а аппаратное обеспечение последовало бы их примеру.
источник
Целочисленные типы без знака в C определены таким образом, чтобы подчиняться правилам абстрактного алгебраического кольца. Например, для любых значений X и Y добавление XY к Y даст X. Целочисленные типы без знака гарантированно подчиняются этим правилам во всех случаях, которые не включают преобразование в любой другой числовой тип или из него [или типы без знака разных размеров] , и эта гарантия - одна из важнейших особенностей таких типов. В некоторых случаях стоит отказаться от возможности представлять отрицательные числа в обмен на дополнительные гарантии, которые могут предоставить только беззнаковые типы. Типы с плавающей запятой, независимо от того, подписаны они или нет, не могут соответствовать всем правилам алгебраического кольца [например, они не могут гарантировать, что X + YY будет равно X], и действительно, IEEE этого не делает. t даже позволяют им соблюдать правила класса эквивалентности [требуя, чтобы определенные значения сравнивались не равными самим себе]. Я не думаю, что «беззнаковый» тип с плавающей запятой может подчиняться каким-либо аксиомам, которых не может придерживаться обычный тип с плавающей запятой, поэтому я не уверен, какие преимущества он может предложить.
источник
IHMO это потому, что поддержка как подписанных, так и неподписанных типов с плавающей запятой в аппаратном или программном обеспечении была бы слишком хлопотной.
Для целочисленных типов мы можем использовать одну и ту же логическую единицу как для знаковых, так и для беззнаковых целочисленных операций в большинстве ситуаций, используя свойство nice дополнения 2, потому что результат идентичен в этих случаях для сложения, подпрограммы, нерасширяющего mul и большинства побитовых операций. Для операций, которые различают подписанную и неподписанную версию, мы все же можем разделить большую часть логики . Например
INT_MIN
. Также теоретически возможно, он, вероятно, не используется на оборудовании, но он полезен в системах, которые поддерживают только один тип сравнения (например, 8080 или 8051).Системы, использующие дополнение 1, также нуждаются в небольшой модификации логики, потому что это просто бит переноса, обернутый вокруг младшего значащего бита. Не уверен насчет знаково-величинных систем, но похоже, что они используют внутреннее дополнение до единицы, поэтому применимо то же самое.
К сожалению, у нас нет такой роскоши для типов с плавающей запятой. Просто освободив знаковый бит, мы получим версию без знака. Но тогда для чего нам использовать этот бит?
Но оба варианта требуют сумматора большего размера, чтобы приспособиться к более широкому диапазону значений. Это увеличивает сложность логики, в то время как верхний бит сумматора большую часть времени остается неиспользованным. Еще больше схем потребуется для умножения, деления или других сложных операций.
В системах, которые используют программное обеспечение с плавающей запятой, вам нужны 2 версии для каждой функции, чего не ожидалось в то время, когда память была настолько дорогой, или вам нужно было бы найти какой-то "хитрый" способ совместного использования частей подписанных и неподписанных функций
Однако оборудование с плавающей запятой существовало задолго до изобретения C , поэтому я считаю, что выбор C был обусловлен отсутствием аппаратной поддержки по причине, о которой я упоминал выше.
Тем не менее, существует несколько специализированных форматов беззнаковых чисел с плавающей запятой, в основном для целей обработки изображений, таких как 10- и 11-битные типы с плавающей запятой группы Khronos.
источник
Я подозреваю, что это потому, что базовые процессоры, на которые нацелены компиляторы C, не имеют хорошего способа работы с числами с плавающей запятой без знака.
источник
Хороший вопрос.
Если, как вы говорите, это только для предупреждений во время компиляции и никаких изменений в их поведении, в противном случае базовое оборудование не затронуто, и, как таковое, это будет только изменение C ++ / компилятора.
Раньше я искал то же самое, но дело в том, что это мало поможет. В лучшем случае компилятор может находить статические назначения.
Или минималистично длиннее
Но это все. С целыми типами без знака вы также получаете определенный цикл, а именно, он ведет себя как модульная арифметика.
после этого 'uc' содержит значение 255.
Теперь, что бы компилятор сделал с тем же сценарием, учитывая беззнаковый тип с плавающей запятой? Если значения не известны во время компиляции, потребуется сгенерировать код, который сначала выполняет вычисления, а затем выполняет проверку знака. Но что, если результатом такого вычисления будет «-5,5» - какое значение должно быть сохранено в объявлении с плавающей запятой без знака? Можно было бы попробовать модульную арифметику, как для целочисленных типов, но это приходит со своими проблемами: Самое большое значение неоспоримо бесконечность .... это не работает, вы не можете иметь «бесконечность - 1». Поиск самого большого отличного значения, которое он может удерживать, также не будет работать, так как вы столкнетесь с его точностью. "NaN" будет кандидатом.
Наконец, это не будет проблемой с числами с фиксированной точкой, поскольку там модуль хорошо определен.
источник