使用uint64_t

问题描述

我有一个高度可移植的库(即使没有内核也可以在任何地方编译并很好地工作),我希望它保持尽可能的可移植性。到目前为止,我避免使用64位数据类型,但现在可能需要使用它们-确切地说,我需要64位位掩码。

我从来没有真正考虑过这一点,并且我还不是足够的硬件专家(尤其是嵌入式系统方面的专家),但是我现在想知道:使用uint64_t(或等效地,{{1 }})?我可以想到两种解决方法:

  1. 实际的可移植性:包括8位CPU在内的所有微控制器都能够处理64位整数吗?
  2. 性能:与32位整数相比,8位CPU对64位整数执行按位运算有多慢?我正在设计的函数只有一个64位变量,但是会对其执行很多按位运算(即循环)。

解决方法

对符合标准的C编译器有各种最低要求。 C语言允许两种形式的编译器:托管独立式。托管意味着可以在操作系统之上运行,并且可以在没有操作系统的情况下独立运行。大多数嵌入式系统编译器都是独立的实现。

独立式编译器有一些余地,它们不需要支持所有标准库,但是需要支持它们的最小子集。这包括stdint.h(请参阅C17 4/6)。进而需要编译器实现以下内容(C17 7.20.1.2/3):

以下类型是必需的:

int_least8_t int_least16_t int_least32_t int_least64_t
uint_least8_t uint_least16_t uint_least32_t uint_least64_t

因此,微控制器编译器不需要支持uint64_t,但是它必须(足够多)支持uint_least64_t。实际上,这意味着编译器也可能会添加uint64_t支持,因为在这种情况下是同一件事。

关于8位MCU支持的内容...它通过指令集支持8位算术运算,在某些特殊情况下,还使用索引寄存器进行了16位运算。但是通常,只要使用大于8位的类型,它就必须依赖软件库。

因此,如果您尝试对8位二进制数进行32位算术运算,则它将内联代码的某些编译器软件库,结果将是数百个汇编程序指令,从而使此类代码非常低效且占用内存。 64位甚至更糟。

在缺少FPU的MCU上具有浮点数的东西,它们也将通过软件浮点库生成极其低效的代码。


为说明起见,请看一下这个未优化的代码,该代码针对8位AVR(gcc)上的一些非常简单的64位加法:https://godbolt.org/z/ezbKjY
它实际上支持uint64_t,但是编译器产生了大量的开销代码,大约100条指令。在中间,调用隐藏在可执行文件中的内部编译器函数call __adddi3

如果启用优化,我们将得到

add64:
        push r10
        push r11
        push r12
        push r13
        push r14
        push r15
        push r16
        push r17
        call __adddi3
        pop r17
        pop r16
        pop r15
        pop r14
        pop r13
        pop r12
        pop r11
        pop r10
        ret

我们将不得不深入研究库源代码或单步调试程序集,以查看__adddi3中有多少代码。我想这仍然不是一个微不足道的功能。

如您所愿,在8位CPU上执行64位算术是一个非常糟糕的主意。

,

好吧,如果您最关心的是保持适当的兼容性,这就是避免使用64位数字的原因,那么为什么不使用int整数数组,而考虑使用一个完整整数可以存储30位。

我建议您看一下有关使用位掩码(大于32位)表示标准字符的标准库资源。 select(2)系统调用触及的文件,以及如何使用FDSET宏。

真实的情况是,您可能遇到以下问题:确定是否超过用于表示位图的数据类型中的32位限制,或者使用仍然可用的64位类型(暂时)解决问题。当您遇到64位的位掩码,而您最终将不得不越线时,这将是下一个扩展问题。

作为练习,您现在可以执行此操作,并且您将了解到,最后的数据类型或多或少是一组大位,您可以根据需要使用它们。您是否打算使用80位long double值存储大于64位的位掩码?我想您不会,所以考虑一下阵列解决方案,那可能会永远解决您的问题。

如果您的问题是我的情况,我将编写一个32位无符号数字数组,因此所有位在移位,位操作等方面表现均相同。


#define FDSET_TYPE(name,N)  unsigned int name[((N) + 31U) >> 5]
#define FDSET_ISSET(name,N) ((name[(N) >> 5] & 1 << (N & 0x1f)) != 0)

...

    FDSET_TYPE(name,126);

...

    if (FDSET_ISSET(name,35)) { ...

在上面的示例中,FDSET_TYPE宏允许您声明作为第二个参数传递的位数的变量,并使用无符号的32位整数数组将其实现,并舍入为下一个值允许包含所有位。 FDSET_ISSET(name,35)计算所请求的位所在的单元格和偏移量,并用将您传递的数字除以32的余数来屏蔽它---但是当我们选择2的幂时,y使用{{ 1}}屏蔽数字的最后5位,以获取余数mod 32)。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...