🐟
オーバーフローするカウンタ差分計算をC#で正しく実装する方法
はじめに
ハードウェアカウンタをソフトウェアで実装する際、一定ビット幅でオーバーフロー(ロールオーバー)するカウンタの前回値と現在値から正しく「増分」を計算することはよくある課題です。
本稿では、以下のポイントを押さえつつ、C#での実装例を交えて解説します。
- カウンタのオーバーフロー問題
- 分岐+剰余演算による差分計算
- ビットマスクを用いた高速差分計算
- 浮動小数点と整数キャストの使い分け
1. カウンタのオーバーフロー問題
ハードウェアカウンタは、Nバイト幅(例:2バイトなら0~65535)で増加し、最大値を超えると0に戻ります。
このとき、前回値 prev
→ 現在値 curr
の差分を単純に curr - prev
すると負の値になってしまい、累積値の計算が正しく行えません。
例:16bitカウンタ(0~65535)
prev = 65530
curr = 10
単純差分 = 10 - 65530 = -65520 ← 不正
2. 分岐+剰余演算による差分計算
オーバーフロー考慮の基本として、「現在値が前回値以上なら普通に引き算、そうでなければ一周分を加えて剰余を取る」方法があります。
実装例
var byteSize = 2;
var maxValue = Math.Pow(2, 8 * byteSize); // 2^(8×バイト数)
double rawDiff = ((value - lastValue) + maxValue) % maxValue;
double delta = Math.Floor(rawDiff * Math.Pow(10, decimalPoint))
/ Math.Pow(10, decimalPoint);
-
(value - lastValue) + maxValue
で負の値を回避 -
% maxValue
で 0~(maxValue−1) の範囲に収める -
Math.Floor
による切り捨て丸め
3. ビットマスクを用いた高速差分計算
整数キャスト+ビットマスクを使えば、剰余演算よりも高速に差分を得られます。ただし対象は整数型に限られます。
手順
-
整数にキャスト
ulong currU = (ulong)Math.Round(value); ulong lastU = (ulong)Math.Round(lastValue);
-
マスクの生成
int bits = 16; ulong mask = ((ulong)1 << bits) - 1; // 下位 bits ビットを 1 に
-
差分計算
ulong diffU = (currU - lastU) & mask; double delta = (double)diffU;
-
currU < lastU
のとき自動的にラップアラウンド - 下位 N×8 ビットだけを残す
4. 浮動小数点 vs 整数キャスト
方法 | 特徴 |
---|---|
剰余演算 (% ) |
double のまま計算可能。可読性高いが若干遅いことも |
ビットマスク (& mask ) |
整数キャストが必要。高速で厳密なビット操作が可能 |
- 浮動小数点の値をそのまま扱いたい場合:剰余演算
- 厳密かつ高速に処理したい場合:整数キャスト+ビットマスク
5. C#での総合サンプル
public double GetResult(double value, int decimalPoint)
{
// カウンタ幅と最大値
int byteSize = 2;
double maxVal = Math.Pow(2, 8 * byteSize);
// 剰余演算による差分計算
double rawDiff = ((value - lastValue) + maxVal) % maxVal;
// 切り捨て丸め
double scale = Math.Pow(10, decimalPoint);
double delta = Math.Floor(rawDiff * scale) / scale;
// 前回値更新
lastValue = value;
return delta;
}
まとめ
- オーバーフロー を正しく扱うには「一周分を加えてから剰余」または「ビットマスク」で下位ビットを抽出
- 浮動小数点のまま実装するなら
% maxValue
が手軽 - 高速・厳密さ重視なら整数キャスト+
& mask
-
lastValue == value
でもゼロが返るよう、分岐や演算式を工夫する
今回は、仕様の都合で浮動小数点についても書きましたが、基本的には整数値で取り扱う方が様々な面でメリットでます。実情にあわせ、ハードウェアカウンタ差分を実装していただければと思います。
Discussion