I am writing a class to setup a serial port on an AVR microcontroller. I have a template function that takes as parameters a cpu clock value and a desired baud rate, does a quick calculation, verifies if the actual value is within 1.5% margin from the desired value with static asserts, then returns the actual value to set inside an 8 bit register. I need to use std::round and its return value needs to be constexpr to have everything evaluated at compile time. This is the problematic bit:
#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;
}
This works fine for x86 on compiler explorer, it returns 4. On AVR there is no cmath, float round(float) is defined in math.h and implemented in assembly, so probably not constexpr. After a quick search I found this: https://stackoverflow.com/a/24348037/11221049 I made a few adjustments, to then have gcc point out that this function is non-constexpr. I made it constexpr, but then its result will never be constexpr because it requires access to a union member that has not been initialized. Union tricks are not constexpr. So... is it possible to make a constexpr round function (knowing that anything from math.h is written directly in assembly)? How is it done in gnu libc++?