div100()

10、100または1000で割った商と余りを計算

10、100または1000で割った商と余りを計算します。

struct div_result_i32 {
		int32_t quo; // quotient
		int16_t rem; // remainder
		uint8_t b_neg;  // true if negative
		uint8_t digits_rem; // digits of remainder
};

div_result_i32 div10(int32_t val);
div_result_i32 div100(int32_t val);
div_result_i32 div1000(int32_t val);

センサー値などで100倍した値をuint16_t型にして受け渡しする場合がありますが、除算回路がないマイコンでの計算処理には相応の時間がかかるため、加算・減算・乗算とビットシフトを用いた近似計算と補正により計算を行います。

valに計算したい値、remは余りの格納変数、negは符号を格納する変数を渡します。

戻り値は商の値(常に正)、remには余りの値(常に正)、negは負ならtrueが格納されます。

計算アルゴリズムの制約(桁あふれ)からdiv100()div1000()での計算可能な値域が決まっています。div100()は-99999~99999までの値に対応し、div1000()は-999999~999999までの値に対応します。

商を得る近似計算式

div100()
  int dv = val * 1311 >> 17;
  
div1000()
  int dv = val * 131 >> 17;

使用例

auto d1 = div100(sns_val.u16temp_object);
auto d2 = div100(sns_val.u16temp_object);

Serial
	<< crlf << format("..Object  = %c%2d.%02d"
									, d1.b_neg ? '-' : '+', d1.quo, d1.rem)
	        << format("  Ambient = %c%2d.%02d"
									, d2.b_neg ? '-' : '+', d2.quo, d2.rem);

計算速度

10倍程度になります。

結果の出力

// 変換オプション
struct DIVFMT {
  static const int STD = 0; // displays with minimul digits (no padding, no positive sign)
  static const int PAD_ZERO = 1; // set padding character as '0' instead of ' '.
  static const int SIGN_PLUS = 2; // put '+' sign if value is positive or 0.
  static const int PAD_ZERO_SIGN_PLUS = 3; // PAD_ZERO & SIGN_PLUS
  static const int SIGN_SPACE = 4; // put ' ' sign if value is positive or 0.
  static const int PAD_ZERO_SIGN_SPACE = 5; // PAD_ZERO & SIGN_SPACE
};

// 文字列変換結果を格納するためのクラス
class _div_chars {
  ...
  const char* begin() const {...}
  const char* end() const {...}
  const char* c_str() const { return begin(); }
  operator const char*() const { return begin(); }
};

// format()メソッド
_div_chars div_result_i32::format(
    int dig_quo = 0, uint32_t opt = DIVFMT::STD) const;

// Serialへのインタフェースの実装
template <class D> class stream {
...
		inline D& operator << (const mwx::_div_chars&& divc);	
		inline D& operator << (const mwx::div_result_i32&&);
		inline D& operator << (const mwx::div_result_i32&);
};

割り算の結果を格納するdiv_result_i32クラスにはformat()メソッドを用い_div_charsクラスオブジェクトを得ることが出来る。_div_chars()クラスオブジェクトは文字列バッファを内包していてconst char*型として文字列バッファにアクセスするメソッドが用意されている。また、Serialオブジェクトに対する<<演算子も実装している。

format()メソッドのパラメータの1番目dig_quoは出力桁数(符号部を含まず)を指定します。出力桁数に足りない場合(以下、不足桁)は空白または0で埋めます。2番目のパラメータoptは書式を指定します。

optパラメータ
内容

DIVFMT::STD

標準的な出力で、不足桁は空白で埋め、負の値に限り-を付加します。

DIVFMT::PAD_ZERO

不足桁は0で埋めます。

DIVFMT::SIGN_PLUS

正の値にも+符号を付加します。

DIVFMT::PAD_ZERO_SIGN_PLUS

不足桁は0で埋め、正の値にも+符号を付加します。

DIVFMT::SIGN_SPACE

正の値の場合は+符号の替わりに空白を付加します。

DIVFMT::PAD_ZERO_SIGN_SPACE

不足桁は0で埋め、正の値の場合は+符号の替わりに空白を付加します。

//// div_result_i32オブジェクトから直接出力
Serial << div100(-1234) << crlf;
// 結果: -12.34 

//// 3桁で出力します
Serial << div100(3456).format(3, DIVFMT::PAD_ZERO_SIGN_PLUE) << crlf;
// 結果: +034.56 

//// c_str()を使ってconst char*を得る
char str1[128];
auto x = div100(-5678);
mwx_snprintf(str1, 16, "VAL=%s", x.format.c_str()); // const char*
Serial << str1;
// 結果: VAL=-56.78

背景

TWELITE 無線マイコンでは除算はコストが高い演算であるため、目的を限定した除算アルゴリズムを追加した。

ライブラリ内では温度・湿度といった一部のセンサー値、100倍の値(25.12℃なら2512)を用いて表現しているため、100で割った商と余りを得るための簡素な手続きを定義した。

dev_result_i32::format()については、書式出力を行う際の煩雑さを避けるためである。

最終更新