Подтвердить что ты не робот

Почему C не имеет неподписанных поплавков?

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

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

Мне нравятся эти предупреждения. Они помогают мне сохранить правильный код.

Почему у нас нет такой же роскоши для поплавков? Квадратный корень, безусловно, никогда не вернет отрицательное число. Есть и другие места, где отрицательное значение float не имеет никакого значения. Идеальный кандидат для неподписанного поплавка.

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

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

Любая идея, почему они не существуют?


EDIT: Я знаю, что у x87 FPU нет инструкций по работе с неподписанными поплавками. Позволяет использовать только подписанные поплавковые инструкции. Неправильное использование (например, переход к нулю) можно рассматривать как поведение undefined так же, как переполнение целых чисел со знаком undefined.

4b9b3361

Ответ 1

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

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

Таким образом, вопрос заключается в том, почему его не поддерживают аппаратные разработчики. И я думаю, что ответ на этот вопрос заключается в том, что первоначально не было стандартного значения беззнакового плавающего элемента. Поскольку языки любят быть обратно совместимыми, даже если они были добавлены, языки не могли его использовать. Чтобы увидеть спецификацию с плавающей запятой, вы должны посмотреть на IEEE standard 754 Floating-Point.

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

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

Ответ 2

(В стороне, Perl 6 позволяет писать

subset Nonnegative::Float of Float where { $_ >= 0 };

а затем вы можете использовать Nonnegative::Float так же, как и любой другой тип.)

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

[править]

C похоже на сборку: то, что вы видите, именно то, что вы получаете. Неявное "Я проверю, что этот float неотрицателен для вас" идет вразрез с его философией дизайна. Если вы действительно этого хотите, вы можете добавить assert(x >= 0) или подобное, но вы должны сделать это явно.

Ответ 3

Существует значительная разница между целыми числами без знака в C/С++:

value >> shift

знаковые значения оставляют верхний бит неизменным (знак расширяется), неподписанные значения очищают верхний бит.

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

float a = 2.0f, b = 10.0f, c;
c = a - b;

Какое значение имеет c? -8. Но что бы это означало в системе без отрицательных чисел. Возможно, FLOAT_MAX - 8? Фактически, это не работает, так как FLOAT_MAX-8 - это FLOAT_MAX из-за прецизионных эффектов, поэтому все еще более отвратительно. Что, если это было частью более сложного выражения:

float a = 2.0f, b = 10.0f, c = 20.0f, d = 3.14159f, e;
e = (a - b) / d + c;

Это не проблема для целых чисел из-за природы системы с двумя дополнениями.

Также рассмотрим стандартные математические функции: sin, cos и tan будут работать только на половину их входных значений, вы не смогли бы найти журнал значений < 1, вы не могли бы решить квадратичные уравнения: x = (-b +/- root (b.b - 4.a.c))/2.a и т.д. Фактически, это, вероятно, не будет работать для какой-либо сложной функции, поскольку они, как правило, реализуются как полиномиальные аппроксимации, которые где-то будут использовать отрицательные значения.

Итак, неподписанные поплавки довольно бесполезны.

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

Ответ 4

Я считаю, что неподписанный int был создан из-за необходимости большего маржи значения, чем мог бы предложить подписанный int.

Поплавок имеет гораздо больший запас, поэтому никогда не было "физической" необходимости для неподписанного поплавка. И, как вы указываете на свой вопрос, дополнительную 1-битную точность не убить.

Edit: Прочитав ответ Брайана Р. Бонди, я должен изменить свой ответ: Он определенно прав, что базовые процессоры не имеют неподписанных операций с плавающей запятой. Тем не менее, я утверждаю, что это было конструктивное решение, основанное на причинах, о которых я говорил выше: -)

Ответ 5

Я думаю, что Treb на правильном пути. Для целых чисел более важно, чтобы у вас был неподписанный соответствующий тип. Это те, которые используются в бит-сдвиге и используются в битовых картах. Знак бит просто мешает. Например, смещение отрицательного значения с правой стороны, результирующее значение представляет собой реализацию, определенную в С++. Выполнение этого с помощью целых чисел без знака или переполнения такого имеет вполне определенную семантику, потому что такого бита нет.

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

Ответ 6

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

wikipedia articla on ieee числа с плавающей точкой

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

Ответ 7

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

C99 поддерживает комплексные числа и типовую форму sqrt, поэтому sqrt( 1.0 * I) будет отрицательным.


В комментариях был отмечен небольшой блеск выше, поскольку я имел в виду макрос sqrt типа, а не функцию, и он вернет скалярное значение с плавающей запятой путем усечения комплекса к его реальному компоненту:

#include <complex.h>
#include <tgmath.h>

int main () 
{
    complex double a = 1.0 + 1.0 * I;

    double f = sqrt(a);

    return 0;
}

Он также содержит мозговой пупок, так как действительная часть sqrt любого комплексного числа положительна или равна нулю, а sqrt (1.0 * I) - sqrt (0.5) + sqrt (0.5) * я не -1.0.

Ответ 8

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

Ответ 9

Беззнаковые целые типы в C определяются таким образом, чтобы подчиняться правилам абстрактного алгебраического кольца. Например, для любого значения X и Y добавление XY в Y даст X. Беззнаковые целочисленные типы гарантируют выполнение этих правил во всех случаях, которые не связаны с преобразованием в любой другой числовой тип [или беззнаковые типы разного размера], и эта гарантия является одной из наиболее важных характеристик таких типов. В некоторых случаях стоит отказаться от возможности представлять отрицательные числа в обмен на дополнительные гарантии, которые могут предоставить только неподписанные типы. Типы плавающей точки, независимо от того, подписаны они или нет, не могут соблюдаться всеми правилами алгебраического кольца [например, они не могут гарантировать, что X + Y-Y будет равно X], и действительно, IEEE даже не позволяет им соблюдать правила класса эквивалентности [требуя, чтобы некоторые значения сравнивались неравномерно с самим собой). Я не думаю, что "беззнаковый" тип с плавающей запятой мог бы выполнять любые аксиомы, которые обычный тип с плавающей запятой не мог, поэтому я не уверен, какие преимущества он может предложить.

Ответ 10

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