145

我知道,这个问题似乎很奇怪。程序员有时想得太多。请继续阅读...

在 CI 中使用signedunsigned整数很多。我喜欢这样一个事实,即如果我执行诸如将有符号整数分配给无符号变量之类的操作,编译器会警告我。如果我比较有符号整数和无符号整数等等,我会收到警告。

我喜欢这些警告。他们帮助我保持我的代码正确。

为什么我们对花车没有同样的奢侈?平方根绝对不会返回负数。还有其他地方负浮点值没有意义。无符号浮点数的完美候选者。

顺便说一句 - 我并不热衷于通过从浮点数中删除符号位可以获得的单个额外精度。我float对现在的 s 非常满意。我有时只想将浮点数标记为无符号,并得到与整数相同的警告。

我不知道任何支持无符号浮点数的编程语言。

知道为什么它们不存在吗?


编辑:

我知道 x87 FPU 没有处理无符号浮点数的指令。让我们只使用签名的浮点指令。滥用(例如低于零)可以被视为未定义的行为,就像未定义有符号整数的溢出一样。

4

13 回答 13

123

为什么 C++ 不支持无符号浮点数是因为没有等效的机器代码操作供 CPU 执行。所以支持它是非常低效的。

如果 C++ 确实支持它,那么您有时会使用无符号浮点数并且没有意识到您的性能刚刚被杀死。如果 C++ 支持它,则需要检查每个浮点运算以查看它是否已签名。而对于进行数百万次浮点运算的程序,这是不可接受的。

所以问题是为什么硬件实现者不支持它。我认为答案是最初没有定义无符号浮点标准。由于语言喜欢向后兼容,即使添加了语言也无法使用它。要查看浮点规范,您应该查看IEEE 标准 754 Floating-Point

您可以通过创建一个封装浮点或双精度的无符号浮点类来解决没有无符号浮点类型的问题,如果您尝试传入负数,则会引发警告。这效率较低,但如果您不频繁使用它们,您可能不会关心轻微的性能损失。

我绝对看到了无符号浮点数的用处。但是 C/C++ 倾向于选择对每个人都最有效的效率而不是安全性。

于 2009-02-04T16:16:34.803 回答
20

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 ( bb - 4.ac)) / 2.a,依此类推。事实上,它可能不适用于任何复杂的函数,因为这些函数往往被实现为多项式近似,它会在某处使用负值。

所以,无符号浮点数是非常没用的。

但这并不意味着范围检查浮点值的类没有用,您可能希望将值限制在给定范围内,例如 RGB 计算。

于 2009-02-05T01:14:36.917 回答
10

(顺便说一句,Perl 6 允许您编写

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

然后你可以Nonnegative::Float像使用任何其他类型一样使用。)

无符号浮点运算没有硬件支持,因此 C 不提供它。C 主要被设计为“便携式组件”,即尽可能靠近金属而不被束缚在特定平台上。

[编辑]

C 就像汇编:所见即所得。一个隐含的“我会检查这个浮点数对你来说是非负的”违背了它的设计理念。如果你真的想要它,你可以添加assert(x >= 0)或类似的,但你必须明确地这样做。

于 2009-02-04T16:24:12.463 回答
9

我相信创建 unsigned int 是因为需要比已签名 int 所能提供的更大的值余量。

浮点数的边距要大得多,因此对无符号浮点数从来没有“物理”需求。正如您在问题中指出的那样,额外的 1 位精度没什么可杀的。

编辑: 阅读Brian R. Bondy 的答案后,我必须修改我的答案:他绝对正确,底层 CPU 没有无符号浮点操作。但是,我坚持认为这是基于我上述原因的设计决定;-)

于 2009-02-04T16:18:51.897 回答
7

我认为 Treb 走在正确的轨道上。对于具有无符号对应类型的整数来说更重要。这些是用于位移位图中的那些。一个符号位刚刚进入。例如,右移一个负值,结果值是 C++ 中定义的实现。使用无符号整数或溢出这样的整数具有完美定义的语义,因为没有这样的位。

所以至少对于整数来说,对单独的无符号类型的需求比仅仅给出警告要强。浮动不需要考虑以上所有点。所以,我认为,对它们的硬件支持并不真正需要,而且 C 那时已经不支持它们了。

于 2009-02-04T16:27:06.747 回答
6

平方根绝对不会返回负数。还有其他地方负浮点值没有意义。无符号浮点数的完美候选者。

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。

于 2009-02-04T16:31:59.687 回答
5

我想这取决于仅对 IEEE 浮点规范进行签名,并且大多数编程语言都使用它们。

关于 IEEE-754 浮点数的维基百科文章

编辑:另外,正如其他人所指出的,大多数硬件不支持非负浮点数,因此由于有硬件支持,因此普通类型的浮点数更有效。

于 2009-02-04T16:12:36.683 回答
3

我认为主要原因是与无符号整数相比,无符号浮点数的用途非常有限。我不相信这是因为硬件不支持它的论点。较旧的处理器根本没有浮点功能,它们都是在软件中模拟的。如果无符号浮点数有用,它们将首先在软件中实现,然后硬件也会效仿。

于 2009-02-04T17:51:20.493 回答
3

C 中的无符号整数类型以遵循抽象代数环规则的方式定义。例如,对于任何值 X 和 Y,将 XY 添加到 Y 将产生 X。无符号整数类型保证在所有不涉及与任何其他数字类型 [或不同大小的无符号类型] 之间转换的情况下遵守这些规则,而这种保证是此类类型最重要的特征之一。在某些情况下,放弃表示负数的能力以换取只有无符号类型才能提供的额外保证是值得的。浮点类型,无论是否有符号,都不能遵守代数环的所有规则[例如,它们不能保证 X+YY 将等于 X],而 IEEE 确实没有 甚至允许他们遵守等价类的规则[通过要求某些值与它们自己比较不相等]。我不认为“无符号”浮点类型可以遵守普通浮点类型不能遵守的任何公理,所以我不确定它会提供什么优势。

于 2014-06-01T21:20:47.980 回答
1

IHMO 这是因为在硬件或软件中同时支持有符号和无符号浮点类型太麻烦了

对于整数类型,我们可以在大多数情况下使用相同的逻辑单元进行有符号和无符号整数运算,使用 2 的补码的好属性,因为在这些情况下,加法、减法、非扩展 mul 和大多数按位运算的结果是相同的。对于区分有符号和无符号版本的操作,我们仍然可以共享大部分逻辑。例如

使用 1 的补码的系统也只需要对逻辑进行一些修改,因为它只是将进位位包裹到最低有效位。不确定符号幅度系统,但似乎它们在内部使用 1 的补码,所以同样适用

不幸的是,我们对浮点类型并不那么奢侈。通过简单地释放符号位,我们将获得无符号版本。但是那我们应该用那个位做什么呢?

  • 通过将范围添加到指数来增加范围
  • 通过将其添加到尾数来提高精度。这通常更有用,因为我们通常需要比范围更高的精度

但是这两种选择都需要一个更大的加法器来适应更广泛的价值范围。这增加了逻辑的复杂性,而加法器的最高位大部分时间都没有使用。乘法、除法或其他复杂运算将需要更多电路

在使用软件浮点的系统上,每个函数都需要 2 个版本,这在内存非常昂贵的时候是意料之中的,或者你必须找到一些“棘手”的方法来共享部分有符号和无符号函数

但是浮点硬件早在 C 发明之前就已经存在了,所以我认为选择 C ​​是由于我上面提到的原因缺乏硬件支持

也就是说,存在几种专门的无符号浮点格式,主要用于图像处理,如Khronos 集团的 10 位和 11 位浮点类型

于 2019-02-27T17:03:32.953 回答
1

我不知道任何支持无符号浮点数的编程语言。知道为什么它们不存在吗?

存在无符号浮点数。参见unsigned float16GPU 硬件的(11 个小数位,5 个指数位,0 个符号位),HDR 格式 DXGI_FORMAT_BC6H。只是它们在大多数计算硬件中并不常见,以至于主流编程语言都忽略了它们。在这种用法中,符号被省略,因为比黑色深的颜色无论如何都没有意义。

即使是更常见的IEEE half or signed float16_t,它在图形和机器学习领域非常频繁地用于 HDR 图像和较低带宽的张量,也没有获得被纳入 C/C++ 的荣誉(尽管,更多的领域- CUDA/HLSL 等特定语言确实有half/ float16_t,并且也有C++ 提议)。因此,即使signed float16不能在编译器特定扩展(例如gcc __fp16)之外进入 C++,那么 anunsigned float16就没有什么希望了:b,甚至 CUDA 或 HLSL在语言中都没有无符号类型,就在纹理定义本身(在 .DDS 文件或 GPU 纹理内存中)。在那之前,我们将不得不通过帮助库在没有编译器帮助的情况下继续实现更多奇特的类型。

于 2021-09-01T22:16:58.357 回答
0

我怀疑这是因为 C 编译器所针对的底层处理器没有处理无符号浮点数的好方法。

于 2009-02-04T16:13:18.460 回答
0

好问题。

如果,如您所说,它仅用于编译时警告并且它们的行为没有改变,否则底层硬件不会受到影响,因此它只会是 C++/编译器的更改。

我以前也有同样的想法,但问题是:这没有多大帮助。编译器充其量只能找到静态分配。

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”将是一个候选人。

最后,这不会是定点数的问题,因为模数是明确定义的。

于 2020-04-23T07:10:18.250 回答