任意固定小数点→浮動小数点変換スニペット
I2Cセンサーとかを扱うと固定小数点表現によく出会う。が、固定小数点のままだと計算がめんどうなので、とりあえず浮動小数点に変換しときたいというケースがまぁまぁある。
そういうときに雑に使えるスニペットがほしかったので書いた。
#include <type_traits>
template <uint8_t int_bits, uint8_t fractional_bits, class T>
inline float fixed_point_to_float(const T fixed) {
static_assert(std::is_unsigned<T>::value, "argument must be unsigned");
constexpr uint8_t msb = int_bits + fractional_bits - 1;
constexpr T mask = static_cast<T>(~(( static_cast<T>(~0)) << msb));
constexpr float deno = 1<<fractional_bits;
if (fixed & (1<<msb)) {
// negative
return -( ( (~fixed & mask) + 1) / deno);
} else {
// positive
return fixed / deno;
}
} type_traits がない環境の場合、include と static_assert を消すだけで動く。これはエラーチェックにしか使ってなくて、もし消したとしても、負の signed を渡すと左シフトが不正になるのでエラーになる。
センサ出力とかの場合、8bit単位のビット数ではないことが多いので、渡された型のサイズに関わらずに処理できるようにマスクを作っている。
// usage
int main (int argc, char* argv[]) {
// from ADT7410 datasheet
is(fixed_point_to_float<9, 4>((uint16_t)0b0000000000001), 0.0625);
is(fixed_point_to_float<9, 4>((uint16_t)0b0100101100000), 150.0);
is(fixed_point_to_float<9, 4>((uint16_t)0b0000000000000), 0);
is(fixed_point_to_float<9, 4>((uint16_t)0b1110010010000), -55.0);
is(fixed_point_to_float<9, 4>((uint16_t)0b1111111111111), -0.0625);
is(fixed_point_to_float<9, 4>((uint32_t)0b1111111111111), -0.0625 );
/** compile error : argument must be unsigned
printf("%f\n", fixed_point_to_float<9, 4>((int16_t)0b1111111111111));
*/
// from MCP3425 datasheet
is(fixed_point_to_float<1, 11>((uint16_t)0x001) * (2.048), 1e-3);
is(fixed_point_to_float<1, 13>((uint16_t)0x001) * (2.048), 250e-6);
is(fixed_point_to_float<1, 15>((uint16_t)0x001) * (2.048), 62.5e-6);
// from MPL115A2 datasheet
// a0 coefficient
is(fixed_point_to_float<13, 3>((uint16_t)0x3ECE), 2009.75);
// b1 coefficient
is(fixed_point_to_float<3, 13>((uint16_t)0xB3F9), -2.37585);
// b2 coefficient
is(fixed_point_to_float<2, 14>((uint16_t)0xC517), -0.92047);
// c12 coefficient
is(fixed_point_to_float<1, 15>((uint16_t)0x33C8)/(1<<9), 0.000790);
// test dynamic variable
volatile uint16_t x = 0x001;
is(fixed_point_to_float<1, 11>(x) * (2.048), 1e-3);
} テンプレートの第1引数は整数部(符号込み)のビット数・第2引数は小数点分のビット数
これはQ表記に対応する。
Q表記だと Q1.15 だと符号分1・整数部なし・15ビットの小数点桁。Q9.4 だと符号付き整数部8bit、小数部4bit。
メモ
固定小数点数用のクラス作って可能な限りは固定小数点で演算したほうがいい気はする。ヘッダ1ファイルとかで使えるの、当然もうありそうだけど見つけられてない。
関連エントリー
- ESP8266 Arduino で CO2 センサー MH-Z19 を読む 寝室に置いてみたいので ESP8266 (ESP-WROOM-02) で動かして GrowthForecast にポストするようにしてみた。...
- MeArm っぽいロボットアームの制御 MeArm のパクりっぽいやつ(設計はオープンだからパクりとはいわない気はする)を AliExpress で買ってみました。https://...
- 中華 NanoVNA にバッテリー表示をつける 回路図の D2 はバッテリーから MCU の VBAT に接続する経路ですが、自分の入手した固体だと未実装でした。せっかくなので、手元にあっ...
- C++ でビットフィールドを再発明する ビットフィールドとは C/C++にはほとんど使われてないがビットフィールドという機能がある。 union { uint8_t raw; st...
- STM32F103 で内部温度センサを読み出す 内部に温度センサを内蔵しており、追加の部品なしにある程度の温度は知ることができる。あまり正確とはいえないが相対的に温度の動きを見るには十分 ...