セクションの複数ページをまとめています。 印刷またはPDF形式で保存...

もとのページに戻る

2024-09-06 現在

div10(), div100(), div1000()

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までの値に対応します。

    使用例

    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分の1程度になります。

    結果の出力

    // 変換オプション
    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 BLUE/RED では除算はコストが高い演算であるため、目的を限定した除算アルゴリズムを追加した。

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

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