3

秒単位の時間の固定小数点表現が必要であり、ティック間の時間がその固定小数点形式で正確に表現できない場合、単純な組み込みシステムで時間を追跡するにはどうすればよいでしょうか? そのような状況で累積エラーをどのように回避しますか。

この質問は、 slashdot に関するこの記事への反応です。

1/3 を 10 進固定小数点数として適切に表現できないのと同様に、0.1 秒を 2 進固定小数点数として適切に表現することはできません。2 進固定小数点表現には小さな誤差があります。たとえば、ポイントの後に 8 バイナリ ビットがある場合 (つまり、256 でスケーリングされた整数値を使用)、0.1 x 256 は 25.6 であり、25 または 26 に丸められ、-2.3% のオーダーのエラーが発生します。またはそれぞれ+1.6%。ポイントの後にバイナリ ビットを追加すると、このエラーの規模は小さくなりますが、なくすことはできません。

加算を繰り返すと、誤差は徐々に蓄積されます。

どうすればこれを回避できますか?

4

6 に答える 6

7

1 つのアプローチは、この 0.1 秒の定数を繰り返し加算して時間を計算しようとするのではなく、単純な整数クロック ティック カウントを維持することです。このティック カウントは、必要に応じて秒単位の固定小数点時間に変換できます。通常は、乗算と除算を使用します。中間表現に十分なビットがある場合、このアプローチは合理的なスケーリングを可能にし、エラーを蓄積しません。

たとえば、現在のティック カウントが 1024 の場合、256 を掛けてから 10 で割ることによって、または同等に、128 を掛けてから 5 で割ることによって、現在の時刻 (ポイントの後に 8 ビットの固定小数点) を取得できます。いずれにせよ、誤差 (除算の剰余) がありますが、剰余は常に 5 未満であるため、誤差は制限されます。累積誤差はありません。

于 2009-10-31T15:10:59.170 に答える
2

別のアプローチは、整数の乗算と除算がコストがかかりすぎると見なされるコンテキストで役立つ場合があります (最近ではかなりまれになっているはずです)。Bresenhams の線画アルゴリズムからアイデアを借りています。現在の時刻を (ティック カウントではなく) 固定小数点で保持しますが、誤差項も保持します。誤差項が大きくなりすぎた場合は、時間値に補正を適用して、誤差が累積しないようにします。

ポイントの後の 8 ビットの例では、0.1 秒の表現は 25 (256/10) で、エラー項 (剰余) は 6 です。各ステップで、エラー アキュムレータに 6 を追加します。これまでのところ、最初の 2 つのステップは...

Clock  Seconds  Error
-----  -------  -----
 25    0.0977    6
 50    0.1953   12

2 番目のステップで、エラー値がオーバーフローしました - 10 を超えました。したがって、クロックをインクリメントし、エラーから 10 を引きます。これは、エラー値が 10 以上になるたびに発生します。

したがって、実際のシーケンスは...

Clock  Seconds  Error  Overflowed?
-----  -------  -----  -----------
 25    0.0977    6
 51    0.1992    2      Yes
 76    0.2969    8
102    0.3984    4      Yes

ほとんどの場合、エラーが発生します (エラー値が 0 の場合にのみ、クロックは正確に正確です) が、エラーは小さな定数によって制限されます。クロック値に累積誤差はありません。

于 2009-10-31T15:15:20.473 に答える
0

0.1 秒カウンターを用意して、10 回ごとに秒カウンターをインクリメントし、0.1 カウンターを 0 に戻してはどうでしょうか?

于 2009-10-31T17:38:29.623 に答える
0

この特定の例では、単純に時間カウントを 10 分の 1 秒 (またはミリ秒、またはアプリケーションに適した時間スケール) で保持します。私はこれを小さなシステムや制御システムで常に行っています。

したがって、100 時間の時間値は3_600_000ティックとして保存されます - エラーはゼロです (ハードウェアによって導入される可能性のあるエラーを除く)。

この単純な手法によって引き起こされる問題は次のとおりです。

  • より大きな数を考慮する必要があります。たとえば、32 ビット カウンターではなく 64 ビット カウンターを使用する必要がある場合があります。
  • すべての計算では、使用される単位を認識する必要があります。これは、問題を引き起こす可能性が最も高い領域です。私はこの問題を解決するために、統一されたユニットのタイム カウンターを使用しています。たとえば、この特定のカウンターでは毎秒 10 ティックしか必要ありませんが、別のカウンターではミリ秒の精度が必要になる場合があります。その場合、両方のカウンターをミリ秒の精度にすることを検討して、実際にはその精度が必要ない場合でも、同じ単位を使用するようにします。

また、「通常」ではないタイマーを使用して、他のトリックを実行する必要がありました。たとえば、私は 1 秒間に 300 回のデータ取得が必要なデバイスで作業しました。ハードウェア タイマーが 1 ミリ秒に 1 回作動しました。ミリ秒タイマーを正確に 1/300 秒単位にスケーリングする方法はありません。そのため、取得がドリフトしないように、3、3、および 4 ティックごとにデータ取得を実行するロジックが必要でした。

ハードウェアの時刻エラーに対処する必要がある場合は、複数の時刻ソースが必要であり、それらを一緒に使用して全体の時刻を同期させます。必要に応じて、これは単純にもかなり複雑にもなります。

于 2009-10-31T21:01:55.670 に答える
0

ハードウェアのみの解決策は、ハードウェア クロック ティックが非常にわずかに速く実行されるように調整することです。正確には、繰り返し追加されるティック期間値の切り捨てによって生じる累積損失を補うのに十分な速さです。つまり、ハードウェア クロックのティック速度を調整して、固定小数点の tick-duration 値が正確になるようにします。

これは、クロックに使用される固定小数点形式が 1 つしかない場合にのみ機能します。

于 2009-10-31T15:24:20.470 に答える
0

過去に実装されたものを見たことがあります。インクリメント値は固定小数点形式では正確に表現できませんが、分数として表現できます。(これは、「エラー値を追跡する」ソリューションに似ています。)

実際、この場合、問題は少し異なりますが、概念的には似ています。問題は、固定小数点表現自体ではなく、完全倍数ではないクロック ソースからタイマーを導出することでした。32,768 Hz で時を刻むハードウェア クロックがありました (水晶振動子ベースの低電力タイマーで一般的です)。そこからミリ秒タイマーが必要でした。

ミリ秒タイマーは、32.768 ハードウェア ティックごとにインクリメントする必要があります。最初の概算は、公称 0.7% の誤差に対して、33 ハードウェア ティックごとにインクリメントすることです。ただし、0.768 は 768/1000、つまり 96/125 であることに注意して、次のようにします。

  • 「分数」値の変数を保持します。0 で開始します。
  • ハードウェア タイマーが 32 になるまで待ちます。
  • 本当ですが:
    • ミリ秒タイマーをインクリメントします。
    • 「分数」の値に 96 を加算します。
    • 「小数」の値が >= 125 の場合は、そこから 125 を引き、ハードウェア タイマーが 33 をカウントするのを待ちます。
    • それ以外の場合 (「小数」の値が 125 未満)、ハードウェア タイマーが 32 カウントするまで待ちます。

ミリ秒カウンターには短期的な「ジッター」がありますが (32 対 33 のハードウェア ティック)、長期的な平均は 32.768 ハードウェア ティックになります。

于 2009-11-01T06:32:45.017 に答える