是否可以为 AVR 编写一个 constexpr 舍入函数?
Is it possible to write a constexpr rounding function for AVR?
我正在编写一个类来设置AVR微控制器上的串行端口。我有一个模板函数,它将 cpu 时钟值和所需的波特率作为参数,进行快速计算,使用静态断言验证实际值是否与所需值在 1.5% 的余量范围内,然后返回实际值以在 8 位寄存器内设置。我需要使用 std::round,它的返回值需要为 constexpr 才能在编译时评估所有内容。这是有问题的一点:
#include <cmath>
template<int c, int b>
constexpr int UBRRValue() {
// return the value for UBRR register to get close to the desired baud rate
// avoid integer division
return std::round( static_cast<float>(c) / ( 16 * b ) - 1 );
}
int main() {
constexpr auto val = UBRRValue<2000,25>();
return val;
}
这适用于编译器资源管理器上的 x86,它返回 4。 在 AVR 上没有 cmath,float round(float) 在 math.h 中定义并在汇编中实现,所以可能不是 constexpr。快速搜索后,我找到了这个:https://stackoverflow.com/a/24348037/11221049 我做了一些调整,然后让 gcc 指出这个函数是非 constexpr。我把它变成了constexpr,但它的结果永远不会是constexpr,因为它需要访问尚未初始化的工会成员。工会的伎俩不是阴谋诡计。 所以。。。是否可以创建一个 constexpr 圆形函数(知道 math.h 中的任何内容都是直接在汇编中编写的)? 在 gnu libc++ 中是如何完成的?
您尝试计算的是(c / (16 * b)) - 1
的正确舍入结果。您正在投射为浮点数以避免整数除法,但如果您无论如何都要在之后舍入,这几乎毫无意义。
请注意,我们可以安全地将-1
移出舍入(只有在由于缺乏浮点精度而丢弃-1
时才会更改结果,而您似乎并不打算这样做)。所以我们所需要的只是c / (16*b)
的正确舍入结果。如果我们将其作为整数除法,我们会得到向下舍入的结果。我们可以通过在除数中增加一半的除数来得到一个中途四舍五入的结果(假设两者都是正数):
template<int c, int b>
constexpr int UBRRValue() {
// return the value for UBRR register to get close to the desired baud rate
return (c + 8*b) / (16 * b) - 1;
}
以下是它通过的一些测试用例: https://godbolt.org/z/Va6qDT
舍入浮点值始终可以通过简单地添加或减去 0.5 来完成,然后转换回整数。无需从 std:: 命名空间调用任何内容。
constexpr int round(double x) {
return (x >= 0.0) ? int(x + 0.5) : int(x - 0.5);
}
constexpr int UBRRValue(int c, int b) {
return round(static_cast<double>(c) / (16 * b) - 1);
}
int main() {
constexpr auto val = UBRRValue(2000, 25);
return val;
}
如果您确定函数始终是常量计算的,则可以安全地使用双精度而不是浮点数,因为它们无论如何都不会进入闪存。
/编辑 正如评论中提到的,这种"总是"有效的说法是不正确的。但是,这种情况就足够了,因为波特率寄存器既不是负数也不是0。
- 当一个值是非常量但用常量表达式初始化时使用constexpr
- 有没有可能制作一个 constexpr 树?
- 为什么 std::launder 是一个 constexpr 函数?
- 我可以定义一个 constexpr 匿名/未命名变量吗?
- 是否已经有一个 constexpr std::bit_cast 与 g++ 一起使用
- 为什么我不能声明一个 constexpr 本地,而一个 const 一个工作?
- 我可以使用 constexpr 函数声明一个静态数组吗?
- 我可以在模板参数中声明一个 constexpr lambda 吗?
- 用另一个 constexpr 数组对成员数组进行大括号初始化
- 有没有办法构造一个 constexpr 函数来获取双精度的位表示
- 是否可以为 AVR 编写一个 constexpr 舍入函数?
- 如何以静态方式使用另一个 constexpr 数组初始化一个数组
- 为什么在这种情况下它不是一个 constexpr?
- 将对静态存储 cstyle 数组的引用从 constexpr 函数传递到另一个 constexpr 函数
- 如何使用 std::copy 将一个 constexpr 数组复制到另一个 constexpr 数组
- 初始化一个constexpr std ::数组,其大小的n维std ::数组
- 我什么时候可以确定一个 constexpr 全局变量会像 C 宏一样被"forgotten"?
- 将一个 constexpr 数组初始化为其他两个 constexpr 数组的总和
- 一个 constexpr 比 const 更"constant"吗?
- POD结构体的值初始化是一个constexpr