我知道,这个问题似乎很奇怪。程序员有时会想太多。请继续阅读...
在C中,我使用signed
和unsigned
整数。我喜欢这样一个事实:如果我执行诸如将有符号整数分配给无符号变量之类的操作,编译器会发出警告。如果我将带符号与无符号整数进行比较,我会得到警告。
我喜欢这些警告。它们帮助我保持我的代码正确。
为什么我们不能为花车提供同样的奢侈品?平方根绝对不会返回负数。还有其他地方负浮动值没有意义。无符号浮点数的完美候选者。
顺便说一句 - 我并不是真的热衷于通过从浮点数中删除符号位而获得的单一额外精度。我对他们现在的float
非常满意。我有时想将浮点数标记为无符号,并获得与整数相同的警告。
我不知道任何支持无符号浮点数的编程语言。
知道为什么他们不存在?
我知道x87 FPU没有处理无符号浮点数的指令。让我们使用带符号的浮点指令。滥用(例如,低于零)可以被认为是未定义的行为,就像未定义有符号整数的溢出一样。
答案 0 :(得分:102)
为什么C ++不支持无符号浮点数是因为CPU没有等效的机器代码操作来执行。因此支持它是非常低效的。
如果C ++确实支持它,那么你有时会使用unsigned float而没有意识到你的性能刚被杀死。如果C ++支持它,则需要检查每个浮点操作以查看它是否已签名。对于执行数百万次浮点运算的程序,这是不可接受的。
所以问题就是为什么硬件实施者不支持它。我认为答案是最初没有定义无符号浮点标准。由于语言喜欢向后兼容,即使添加了语言也无法使用它。要查看浮点规范,您应该查看IEEE standard 754 Floating-Point。
您可以通过创建一个封装浮点数或双精度的无符号浮点类来避免使用无符号浮点类型,如果尝试传入负数,则会抛出警告。这样效率较低,但是如果你没有强烈地使用它们,你可能不会关心那种轻微的性能损失。
我肯定看到有一个无符号浮点数的用处。但是C / C ++倾向于选择效率最高的安全性。
答案 1 :(得分:13)
C / C ++中有符号和无符号整数之间存在显着差异:
value >> shift
有符号值保持最高位不变(符号扩展),无符号值清除最高位。
没有无符号浮点数的原因是,如果没有负值,您会很快遇到各种问题。考虑一下:
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;
由于2的补码系统的性质,这不是整数的问题。
还要考虑标准的数学函数:sin,cos和tan只能用于输入值的一半,你找不到值的对数< 1,你无法解决二次方程:x =( - b +/- root(b.b - 4.a.c))/ 2.a,依此类推。事实上,它可能不适用于任何复杂的函数,因为这些函数往往被实现为在某处使用负值的多项式近似。
所以,无符号浮点数很没用。
但这并不意味着一个范围检查浮点值的类没用,你可能想要将值钳位到给定范围,例如RGB计算。
答案 2 :(得分:9)
(另外,Perl 6允许你写
subset Nonnegative::Float of Float where { $_ >= 0 };
然后您可以像使用任何其他类型一样使用Nonnegative::Float
。)
对无符号浮点运算没有硬件支持,因此C不提供它。 C主要设计为“便携式装配”,即尽可能靠近金属而不被束缚在特定平台上。
[编辑]
C就像汇编:你看到的正是你得到的。隐含的“我会检查这个浮动对你来说是非负的”违背了它的设计理念。如果你真的想要它,你可以添加assert(x >= 0)
或类似的,但你必须明确地这样做。
答案 3 :(得分:7)
我认为unsigned int的创建是因为需要比signed int提供的更大的值余量。
浮点数具有更大的余量,因此对于无符号浮点数从不存在“物理”需求。正如你在自己的问题中指出的那样,额外的1位精度无可挽回。
修改强> 阅读answer by Brian R. Bondy后,我必须修改我的答案: 他绝对是正确的,底层CPU没有无符号浮点运算。但是,我坚持认为这是基于我上述原因的设计决定; - )
答案 4 :(得分:6)
我认为Treb走在正确的轨道上。对于具有无符号对应类型的整数,这一点更为重要。这些是用于位移并用于位图的那些。一个标志位刚刚开始。例如,右移一个负值,结果值是在C ++中定义的实现。使用无符号整数或溢出这样的整数就可以完美地定义语义,因为在这种情况下没有这样的位。
因此,至少对于整数,需要单独的无符号类型比仅发出警告更强。对于花车,不需要考虑以上所有要点。因此,我认为没有真正需要对它们提供硬件支持,而C此时已经不支持它们了。
答案 5 :(得分:5)
我想这取决于IEEE浮点规范只有签名并且大多数编程语言都使用它们。
wikipedia articla on ieee floating point numbers
编辑:另外,正如其他人所指出的,大多数硬件都不支持非负浮点数,因此正常类型的浮点数更有效,因为有硬件支持。
答案 6 :(得分:4)
平方根肯定永远不会返回负数。还有其他地方负浮动值没有意义。无符号浮点数的完美候选者。
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)* I不是-1.0。
答案 7 :(得分:3)
我认为主要原因是与无符号整数相比,无符号浮点数的用途非常有限。我不认为这是因为硬件不支持它。较旧的处理器根本没有浮点功能,它都是用软件模拟的。如果无符号浮点数很有用,它们将首先在软件中实现,硬件也会如此。
答案 8 :(得分:2)
C中的无符号整数类型以遵循抽象代数环的规则的方式定义。例如,对于任何值X和Y,将XY添加到Y将产生X.无符号整数类型在所有情况下都保证遵守这些规则,这些规则不涉及到任何其他数字类型的转换[或不同大小的无符号类型] ,这种保证是此类型最重要的特征之一。在某些情况下,放弃表示负数的能力是值得的,以换取只有无符号类型才能提供的额外保证。浮点类型,无论是否有符号,都不能遵守代数环的所有规则[例如他们无法保证X + Y-Y等于X],而且IEEE甚至不允许他们遵守等价类的规则[通过要求某些值与自身不相等]。我不认为“无符号”浮点类型可以遵守普通浮点类型所不具备的任何公理,所以我不确定它会提供什么优势。
答案 9 :(得分:1)
我怀疑这是因为C编译器所针对的底层处理器没有很好的方法来处理无符号浮点数。
答案 10 :(得分:0)
IHMO是因为在硬件或软件中同时支持有符号和无符号浮点类型会很麻烦
对于整数类型,我们可以在大多数情况下使用2的补码的nice属性来利用the same logic unit for both signed and unsigned integer operations,因为the result is identical in those cases用于加,乘,非加宽倍数和大多数按位运算。对于区分有符号版本和无符号版本的操作,我们仍然可以共享大部分逻辑。例如
INT_MIN
,可以轻松将带符号比较转换为无符号比较,反之亦然。从理论上讲也是可能的,它可能不在硬件上使用,但是在systems that support only one type of comparison(例如8080或8051)上很有用使用1的补码的系统也只需要对逻辑做一点修改,因为它只是将进位位包裹在最低有效位上。不确定信号量级系统,但似乎use 1's complement internally一样,因此同样适用
不幸的是,对于浮点类型,我们并不是那么奢侈。通过简单地释放符号位,我们将获得未签名的版本。但是那该怎么用呢?
但这两个选择都需要一个更大的加法器,以适应更大的价值范围。这会增加逻辑的复杂性,而加法器的高位大部分时间都坐在那儿。乘法,除法或其他复杂运算将需要更多的电路
在使用软件浮点数的系统上,您需要为每个功能提供2个版本,而这在内存非常昂贵的时候是意料之中的,否则您将不得不找到某种“棘手的”方式来共享已签名部分和未签名的函数
但是floating-point hardware existed long before C was invented,因此,我相信C的选择是由于我上面提到的原因而缺乏硬件支持
也就是说,存在几种 specialized 无符号浮点格式,主要用于图像处理,例如Khronos group's 10 and 11-bit floating-point type
答案 11 :(得分:0)
好问题。
如您所说,如果仅用于编译时警告,并且不对其行为进行任何更改,则基础硬件不会受到影响,因此仅是C ++ / Compiler更改。
我以前也愿意这样做,但事实是: 这没有太大帮助。编译器充其量只能找到静态分配。
unsigned float uf { 0 };
uf = -1f;
或者稍微更长一些
unsigned float uf { 0 };
float f { 2 };
uf -= f;
但是就是这样。 使用无符号整数类型,您还可以获得定义的环绕,即,其行为类似于模块化算术。
unsigned char uc { 0 };
uc -= 1;
此“ uc”后的值为255。
现在,在给定无符号浮点类型的情况下,编译器将如何处理相同的情况? 如果在编译时不知道这些值,则需要生成代码,该代码首先执行计算,然后进行符号检查。但是,当这样的计算结果变成“ -5.5”时,应该将哪个值存储在声明为无符号的浮点数中呢? 人们可以像整数类型一样尝试模块化算术,但这有其自身的问题:最大值无疑是无穷大....这是行不通的,您不能拥有“无穷大-1”。追求它可能拥有的最大的独特价值也并不会真正起作用。 “ NaN”将是候选人。您会丢失所有信息,原来包含的数字是什么-并没有真正的帮助,因为您现在需要专门检查该数字,因此您最好检查一下数字是否对自己有利。
最后,由于定义了模,定点数不会有问题。