是否可以为AVR编写constexpr舍入函数?

问题描述 投票:0回答:2

我正在编写一个类来设置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,因为它需要访问尚未初始化的union成员。联盟技巧不是constexpr。那么......是否可以创建一个constexpr循环函数(知道math.h中的任何内容都是直接在汇编中编写的)?如何在gnu libc ++中完成?

c++ rounding avr constexpr
2个回答
2
投票

你想要计算的是(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
投票

舍入浮点值总是可以通过简单地加上或减去0.5然后再回到整数来完成。无需从std :: namespace调用任何内容。

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;
}

如果你确定这些函数总是被const评估,你可以安全地使用双精度而不是浮点数,因为它们最终都不会在闪存中结束。

/编辑正如评论中所提到的,这种“永远”的说法并不正确。但这对于这种情况就足够了,因为波特率寄存器既不是负数也不是0。

© www.soinside.com 2019 - 2024. All rights reserved.