为啥使用十六进制常量?
Posted
技术标签:
【中文标题】为啥使用十六进制常量?【英文标题】:Why use hexadecimal constants?为什么使用十六进制常量? 【发布时间】:2012-06-10 20:03:31 【问题描述】:有时我会看到以十六进制而不是十进制数字定义的整数常量。这是我从 GL10 课程中提取的一小部分:
public static final int GL_STACK_UNDERFLOW = 0x0504;
public static final int GL_OUT_OF_MEMORY = 0x0505;
public static final int GL_EXP = 0x0800;
public static final int GL_EXP2 = 0x0801;
public static final int GL_FOG_DENSITY = 0x0B62;
public static final int GL_FOG_START = 0x0B63;
public static final int GL_FOG_END = 0x0B64;
public static final int GL_FOG_MODE = 0x0B65;
定义2914
显然比定义0x0B62
更简单,那么可能有一些性能提升吗?我真的不这么认为,从那以后改变它应该是编译器的工作。
【问题讨论】:
它没有任何性能 - 有时是编码约定,有时是位掩码,有时是标志变量,有时只是一个数字 有时它只是为了让初学者感到困惑;) @mallaudin 初学者首先应该学习基础知识。不是 OpenGL。 @TheincredibleJan 错误意见 @SemperAmbroscus 一点也不。 【参考方案1】:这可能是为了组织和视觉清洁。 16 进制与二进制的关系比 10 进制简单得多,因为在 16 进制中,每个数字正好对应 4 位。
请注意,在上面,常量是如何用许多共同的数字分组的。如果它们以十进制表示,那么共同的位就不太清楚了。如果它们有共同的十进制数字,则位模式将不会具有相同程度的相似性。
此外,在许多情况下,希望能够将常量按位或在一起以创建标志组合。如果每个常数的值被限制为只有一个非零位的子集,那么这可以通过一种可以重新分离的方式来完成。使用十六进制常量可以清楚每个值中哪些位是非零的。
还有其他两种合理的可能性:八进制或以 8 为基数仅对每个数字 3 位进行编码。然后是二进制编码的十进制,其中每个数字需要四位,但禁止超过 9 的数字值 - 这将是不利的,因为它不能代表二进制可以的所有可能性。
【讨论】:
您将常量与按位或相结合是正确的......完全忘记了那个......【参考方案2】:“定义 2914 而不是 0x0B62 显然更简单”
我不知道具体情况,但通常情况并非如此。
两个问题中:
A) 2914 的位值是多少? B) 0x0B62 的位值是多少?许多开发人员会更快地更正确地回答 B。 (这也适用于类似的问题)
0x0B62(它是 4 个十六进制数字长,因此它表示一个 16 位数字)
0 的位 = 0000 B = 1011 的位 6 的位 = 0110 2 的位 = 0010->
0000101101100010
(我敢让你对 2914 做同样的事情。)
这是使用十六进制值的一个原因,另一个是值的来源可能使用十六进制(例如规范的标准)。
有时我觉得它很傻,例如:
public static final int NUMBER_OF_TIMES_TO_ASK_FOR_CONFIRMATION = ...;
用十六进制写几乎总是很愚蠢,我敢肯定在某些情况下它不会。
【讨论】:
转换推理比较弱:开始-程序-标准-计算器-输入十进制数-切换到二进制模式-盈利!事实上我的任务栏上确实有计算器,所以省略步骤 1-3。 很抱歉我的观点不够清楚。上面的“计算”几乎是由我和其他人立即完成的——我们通常在您将值粘贴到计算器之前完成。很多时候我发现自己只对一个位(一个标志)感兴趣,所以我不扫描整个字符串只是找到覆盖相关位的字节/数字。 @esej:为了让事情更清楚,比较查找 0x60000000 和 1610612736 的位模式的难度。如果两个数字都有很多非零非 F 数字,则难度差异为不太清楚,但很多实用的十六进制值主要由零和 F 组成。【参考方案3】:例如,应用十六进制 masks 时的可读性。
【讨论】:
【参考方案4】:十进制数和十六进制数之间不会有性能提升,因为代码将被编译为移动代表数字的字节常量。
计算机不做十进制,它们(充其量)做二进制。十六进制映射到二进制非常干净,但是将十进制数转换为二进制需要一些工作。
十六进制大放异彩的一个地方是当您有许多相关项目时,其中许多相似,但略有不同。
// These error flags tend to indicate that error flags probably
// all start with 0x05..
public static final int GL_STACK_UNDERFLOW = 0x0504;
public static final int GL_OUT_OF_MEMORY = 0x0505;
// These EXP flags tend to indicate that EXP flags probably
// all start with 0x08..
public static final int GL_EXP = 0x0800;
public static final int GL_EXP2 = 0x0801;
// These FOG flags tend to indicate that FOG flags probably
// all start with 0x0B.., or maybe 0x0B^.
public static final int GL_FOG_DENSITY = 0x0B62;
public static final int GL_FOG_START = 0x0B63;
public static final int GL_FOG_END = 0x0B64;
public static final int GL_FOG_MODE = 0x0B65;
对于十进制数,很难“注意到”大量不同但相关的项目中的恒定位区域。
【讨论】:
你为什么说“他们[计算机]做(充其量)二进制”?您是否曾经使用过除二进制以外的其他任何功能的计算机?我的印象是,任何与其他基础(如三元)一起工作的计算机大多只是假设性的,其中一些是作为概念验证构建的? (充其量)是一个指标,表明普通计算机中的所有底层操作本质上都是二进制的,而不是本质上设计为更高的幂(三进制、八进制、十进制、十六进制等)。 ) 我不得不处理三态逻辑,但不是基于三态逻辑的完整计算机。控制系统中的一些开关终端电路具有三种状态,“打开/关闭/失败”,其中失败表示电路无法确认它是否正确打开或关闭。自然,我们只是将其转换为两个二进制位,并分配一个额外的“错误”状态。【参考方案5】:当涉及到大数字时,以十六进制表示它们会使它们更具可读性,因为它们更紧凑。
此外,有时转换为二进制很重要:十六进制数可以很容易地转换为二进制。有些程序员喜欢这样做,这有助于对数字进行位运算。
至于性能提升:不,没有。
【讨论】:
【参考方案6】:您更愿意写0xFFFFFFFF
还是4294967295
?
第一个更清楚地表示全为 1 的 32 位数据类型。当然,许多经验丰富的程序员会认出后一种模式,并暗中怀疑它的真正含义。但是即使在这种情况下,它也更容易出现打字错误等。
【讨论】:
0x 非常适合 RGBA 颜色值,其他情况下好的旧常量 GL_SOMETHING 是最好的。【参考方案7】:十六进制是最接近二进制格式的可读格式。例如,这简化了很多位操作
【讨论】:
【参考方案8】:0xB62 等于 2914 :-)
对于开发人员而言,以十六进制表示的常量比以 10 为基数的整数表示时更容易在脑海中描绘常量的位模式。
这一事实使得十六进制表示更适合 API 中使用的常量,其中位及其位置(例如用作单独的标志)是相关的。
【讨论】:
【参考方案9】:啊,但是 0xDECAFF 既是素数 (1460959) 也是令人愉悦的紫色(RGB 格式)。
对于颜色,十六进制更方便。
FF FF FF 是白色的 00 00 FF 为蓝色 FF 00 00 为红色 00 FF 00 为绿色 很容易将颜色关系视为数字(尽管人眼的伽马和保真度往往会让人看不顺眼,但为了纯粹的数学精度,我们会忽略那些不方便的物理事实!
【讨论】:
【参考方案10】:没有性能提升。
但是,如果这些常量对应于下面的某些位,则大多数程序员更喜欢十六进制(甚至二进制)以使其清晰易读。
例如,可以很容易地看到 GL_EXP2 有 2 位打开,1 位和 0x0800 位(十进制为 2048)。 2049 的十进制值不太清楚。
【讨论】:
不仅如此,一些编程语言没有二进制数据类型,而且正如你所说,与十进制相比,从二进制转换为十六进制再转换回来要容易得多。 0001 == 1 0010 == 2 0011 == 3 ... 1001 == 9 1010 == A ... 1110 == E 1111 == F。有 4 位到 1 个十六进制字母的关系。所以,A3 = 10100011【参考方案11】:有时使用与位相关的算法会更容易。其他时候,它处理位比较,正如我在评论中的陈述,4 位(二进制数字)转换为 1 个十六进制字母,所以,A3 = 10100011。
其他时候,要么好玩,要么打破单调,虽然不熟悉十六进制的人可能会认为你是在用指针做事
int data = 0xF00D;
if ( val != 0xC0FFEE )
data = 0xDECAF;
我有时用它来检查诸如整数之类的东西的边界。例如,您可以使用 0x7FFFFFFF(0x80000000 在许多情况下有效,但 0x7F... 更安全)来获得最大 int 边界。如果您没有具有类似 MAX_INT 的语言,那么设置非常高的错误常数会很方便。该技术也可以扩展,因为对于 64 位,您可以使用 0x7FFFFFFFFFFFFFFF。您可能会注意到 android 使用 0x7___ 进行 R.id 表查找。
我敢打赌,他们这样做是为了清楚起见。您可以轻松地使用整数,但如果您熟悉十六进制,那也不错。看起来他们正在为某些函数保留 x 值。在十进制中,您会执行 0-99 表示错误,100-199 表示其他错误,等等。他们的工作方式不同。
性能方面,您在运行时一无所获,因为编译器(甚至很多汇编程序)最终会将任何格式转换为二进制,无论是十进制、八进制、十六进制、浮点数、双精度等。
【讨论】:
以上是关于为啥使用十六进制常量?的主要内容,如果未能解决你的问题,请参考以下文章