我正在编写一个类来在 AVR 微 Controller 上设置串行端口。我有一个模板函数,它将 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,因为它需要访问一个尚未初始化的 union 成员。 Union 技巧不是 constexpr。 那么...是否有可能制作一个 constexpr round 函数(知道 math.h 中的任何内容都是直接用汇编编写的)? 在 gnu libc++ 中是如何完成的?
最佳答案
您要计算的是 (c/(16 * b)) - 1
的正确舍入结果。您正在转换为 float 以避免整数除法,但如果您之后要四舍五入,这几乎毫无意义。
请注意,我们可以将 -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
关于c++ - 是否可以为 AVR 编写一个 constexpr 舍入函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55382543/