整数mod 10と整数除算10を取得する最も速い方法は?


10

ハードウェアが係数または除算演算をサポートしていない場合、ソフトウェアによる係数/除算をシミュレートするには、さらに多くのCPUサイクルが必要です。オペランドが10の場合、除算と係数を計算するより速い方法はありますか?

私のプロジェクトでは、整数モジュラス10を頻繁に計算する必要があります。特に、PIC16Fで作業していて、LCDに数値を表示する必要があります。サポートする4桁があるため、係数と除算関数(ソフトウェア実装)への4つの呼び出しがあります。つまり、次のようになります。

digit = number % 10;    // call to an expensive function
number /= 10;           // call to an expensive function
somehow_lit_segments();

digit = number % 10;    // call to an expensive function
number /= 10;           // call to an expensive function
somehow_lit_segments();

digit = number % 10;    // call to an expensive function
number /= 10;           // call to an expensive function
somehow_lit_segments();

digit = number % 10;    // call to an expensive function
number /= 10;           // call to an expensive function
somehow_lit_segments();

同様のコードを使用する他の領域があります。


数十回/秒の呼び出しが問題になるのはなぜですか?プロジェクトが完全に機能し、バグがない限り、私は気になりません。
Nick T

メインビジーループでいくつかの数字を継続的に表示すると、ボタンの応答が遅くなることに気づきました。つまり、ボタンが押されたことを検出するには、そのボタンをもう少し長く押す必要があります。これは、システムクロックが32768 Hzで実行されているときに発生します。
ドノタロ2011

割り込みを使用していますか?なぜ32kHz xtalを使用しているのですか?通常、より高速で動作し、アイドル時にスリープ状態に入ると、電力パフォーマンスが低下する可能性があります。
Nick T

割り込みを使用しています。しかし、表示を更新するだけでは、高速振動に切り替える価値はありません。パワー的に。私のプロジェクトのために。寿命の約90%の低速クロックで実行する必要があります。
ドノタロ2011

2
一般的な注意として、Henry S. Warren、Jr.による本Hacker's Delight巧妙なビットいじりトリックのソースです。私は除算の提案を探しましたが、以下の回答のどれよりも優れている10で除算するものはありません。
RBerteig 2011

回答:


11

ここにあるものに基づいて、私が数年前に使用したバイナリからBCDへのアルゴリズムを示します。7セグディスプレイドライバーに外部BCDを使用していたので、結果を出力用のパックされたBCDとして直接適切なポートに書き込むことができました。

PICにハードウェア乗数がある場合、これはかなり高速です。PIC18F97J60を使用していました。PICにハードウェア乗算器がない場合は、乗算にシフト+加算を使用することを検討してください。

これは、符号なし16ビット整数を受け取り、5桁のパックされたBCDを返します。4桁の場合、変更および高速化できます。これはシフト+加算を使用して10での除算を近似しますが、入力範囲が限られているため、この用途には正確です。結果を別の方法でパックし、結果の使用方法に合わせることができます。

void intToPackedBCD( uint16_t n, uint8_t *digits ) {

    uint8_t d4, d3, d2, d1, d0, q;  //d4 MSD, d0 LSD

    d1 = (n>>4)  & 0xF;
    d2 = (n>>8)  & 0xF;
    d3 = (n>>12) & 0xF;

    d0 = 6*(d3 + d2 + d1) + (n & 0xF);
    q = (d0 * 0xCD) >> 11;
    d0 = d0 - 10*q;

    d1 = q + 9*d3 + 5*d2 + d1;
    q = (d1 * 0xCD) >> 11;
    d1 = d1 - 10*q;

    d2 = q + 2*d2;
    q = (d2 * 0x1A) >> 8;
    d2 = d2 - 10*q;

    d3 = q + 4*d3;
    d4 = (d3 * 0x1A) >> 8;
    d3 = d3 - 10*d4;

    digits[0] = (d4<<4) | (d3);
    digits[1] = (d2<<4) | (d1);
    digits[2] = (d0<<4);
}

素晴らしいリンク、ありがとう!速度を最適化するだけでなく、コードサイズも小さくします。乗算を含まないため、リンクから「12ビットバイナリから4 ASCIIの10進数」を実装しました。
ドノタロ2011

8

符号なし整数を想定すると、除算と乗算はビットシフトから形成できます。そして(整数)除算と乗算から、モジュロを導出できます。

10を掛けるには:

y = (x << 3) + (x << 1);

10で割ることはさらに困難です。私はいくつかの除算アルゴリズムを知っています。正しく思い出せば、ビットシフトと減算を使用して10で割る方法はありますが、正確な方法を思い出せません。それが真実でない場合、これは130サイクル未満を管理する除算アルゴリズムです。どのマイクロを使用しているかはわかりませんが、移植する必要がある場合でも、なんらかの方法で使用できます。

編集:誰かがスタックオーバーフローで言い返しましたが、少しのエラーを許容して大きな一時レジスタがある場合、これは機能します:

temp = (ms * 205) >> 11;  // 205/2048 is nearly the same as /10

除算と乗算があるとすると、モジュロは単純です:

mod = x - ((x / z) * z)

6

ダブルダブルアルゴリズムを使用すると、除算せずにバイナリからパックBCDに変換できますシフト加算3のみを使用します。

たとえば、243 10 = 11110011 2をバイナリに変換します

0000 0000 0000   11110011   Initialization
0000 0000 0001   11100110   Shift
0000 0000 0011   11001100   Shift
0000 0000 0111   10011000   Shift
0000 0000 1010   10011000   Add 3 to ONES, since it was 7
0000 0001 0101   00110000   Shift
0000 0001 1000   00110000   Add 3 to ONES, since it was 5
0000 0011 0000   01100000   Shift
0000 0110 0000   11000000   Shift
0000 1001 0000   11000000   Add 3 to TENS, since it was 6
0001 0010 0001   10000000   Shift
0010 0100 0011   00000000   Shift
   2    4    3
       BCD

このアルゴリズムは、使用可能なハードウェア除数がない場合に非常に効率的です。さらに左シフトのみ1を使用しているため、バレルシフターが使用できない場合でも高速です。


4

必要な桁数に応じて、ブルートフォースメソッドを使用できる場合があります(d-入力番号、t-出力ASCII文字列):

t--;
if (d >= 1000) t++; *t = '0'; while (d >= 1000) { d -= 1000; *t += 1; }
if (d >= 100) t++; *t = '0'; while (d >= 100) { d -= 100; *t += 1;}
if (d >= 10) t++; *t = '0'; while (d >= 10) { d -= 10; *t += 1;}
t++; *t = '0' + d;

複数のifをループに変更することもできます。乗算またはルックアップテーブルによって10の累乗が得られます。


2

このアプリケーションノートでは、バイナリからBCDへの変換、およびその逆の変換を含む、BCD演算のアルゴリズムについて説明します。このアプリケーションノートはAVRであるAtmelによるものですが、説明されているアルゴリズムはプロセッサに依存しません。


1

良い答えはありませんが、姉妹サイトのスタックオーバーフローで、除算とモジュロ最適化のまったく同じ主題について素晴らしい議論があります。

ルックアップテーブルを実装するのに十分なメモリがありますか?

Hackers Delightが最適な除算アルゴリズムに関する論文を発表しています。


いいえ、十分なメモリがありません。足し算、引き算、ビットシフトでやりたい。
ドノタロ2011

1

その値をバイナリ形式で保持し、必要に応じてBCDに変換するのではなく(理解しにくい「変換」を使用して)、その値を常にBCDとして保持することを検討しましたか(単純な特別な「BCD増分」および「BCD追加」サブルーチンを使用)。バイナリからBCD "サブルーチンへ)?

かつて、すべてのコンピューターはすべてのデータを10進数字(10ポジションのギア、5つのコードのうち2つの真空管、BCDなど)として保存していましたが、その遺産は今日も残っています。(リアルタイムクロックチップがBCDを使用する理由を参照してください)。


LCDに表示される数値は、-1999〜1999の範囲の変数です。これは温度を示し、バイナリ形式で計算されます。
ドノタロ2011

1

PICListは PICプロセッサをプログラミングする人々のための素晴らしいリソースです。

BCD変換

PIC16F用に特別に最適化された既製のテスト済みのバイナリからBCDへのサブルーチンの使用を検討しましたか?

特に、PICListの人々は、PIC16FでのバイナリからBCDへの変換の最適化に多くの時間を費やしています。これらのルーチン(それぞれ特定のサイズに最適化されたもの)は、「PIC Microcontoller基数変換演算方法」http://www.piclist.com/techref/microchip/math/radix/index.htmにまとめられてい ます。

整数除算とmod

PIC16FのようなCPUでは、定数による除算に特化したサブルーチンは、汎用の「変数Aを変数Bで除算する」ルーチンよりもはるかに高速です。定数(この場合は「0.1」)を「定数乗算/除算のコード生成」http://www.piclist.com/techref/piclist/codegen/constdivmul.htmに置く か、http://www.piclist.com/techref/microchip/math/basic.htmの近くにある缶詰のルーチン。


1

8x8のハードウェア乗算を前提として、次の手順で、範囲0-2559の12ビット数について計算するルーチンを使用して、任意のサイズの数値のdivmod-10を計算できます。

  1. OrigH:OrigLの元の番号を想定
  2. 元の数を2で割り、それをTempH:TempLに格納します
  3. TempL * 51のMSBをTempH * 51のLSBに追加します。それはおおよその商です
  4. 近似商に10を掛けて、値のMSBを破棄します。
  5. 元の数値のLSBからその結果のLSBを引きます。
  6. その値が10以上の場合(最大値は19)、10を減算し、近似商に1を加算します

私は、数値のMSBがWになるdivmodルーチンと、FSRが指すLSBを書くことをお勧めします。ルーチンは商をFSRにポストデクリメントで保存し、残りをWのままにします。32ビット長を10で除算するには、次のようなものを使用します。

  movlw 0
  lfsr 0、_number + 3; MSBを指す
  _divmod10_stepを呼び出す
  _divmod10_stepを呼び出す
  _divmod10_stepを呼び出す
  _divmod10_stepを呼び出す

divmod-6ステップは、51と10ではなく85と6の定数を使用することを除いて、非常に似ています。どちらの場合でも、divmod10_stepは20サイクル(呼び出し/戻りの場合は4)になるため、短いdivmod10になります。約50サイクルで、長いdivmod10は約100になります(最初のステップで特殊なケースを実行すると、数サイクル節約できます)。


1

これは最速ではないかもしれませんが、簡単な方法です。

 a = 65535;

    l = 0;
    m = 0;
    n = 0;
    o = 0;
    p = 0;

    while (a >= 10000)
    {   a -= 10000;
        l += 1;
    }
     while (a >= 1000)
    {   a -= 1000;
        m += 1;
    }
     while (a >= 100)
    {   a -= 100;
        n += 1;
    }
     while (a >= 10)
    {   a -= 10;
        o += 1;
    }
     while (a > 0)
    {   a -= 1;
        p += 1;
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.