2 つの 32 ビット変数に大きな数値、時間 (マイクロ秒) が格納されています。マイクロ秒の時間をミリ秒に変更する方法が必要なので、差の結果を32ビット数に保存できます。
詳細: 2 つの 32 ビット変数に 1 回あります。1 つの変数がより重要なビットを持ち、他の変数がより重要でないビットを持つ場合。今回はマイクロ秒の分解能があるので、ミリ秒に変更したいと思います。では、2 つの変数に格納されている数値をどのように除算するか。
64 ビット タイプがない場合は、次のようにできます。
uint32_t higher, lower; // your input
lower /= 1000;
lower += (higher % 1000) * 4294967L; // approximate 2^32 / 1000
higher /= 1000;
lower
結果がそれ自体に適合する場合、にhigher
なるはずです0
。
@Mikhailが指摘したように、この解決策は概算であり、0.296 * higher + 2
msのエラーがあることに注意してください(何かが欠けていない限り)。
本当により良い精度が必要で、効率を気にしない場合は、途中で少し浮動小数点演算を使用して、結果を正しく丸めることができます。努力する価値があるかどうかは疑問です:
uint32_t higher, lower; // your input
// simpler without a helper variable
if (lower % 1000 >= 500)
{
lower /= 1000;
++lower;
}
else
lower /= 1000;
lower += round((higher % 1000) * 4294967.296); // 2^32 / 1000
higher /= 1000;
する必要がありinclude <cmath>
ますround()
。
注として、この場合の@Mikhailのソリューションはおそらくより優れており、より高速である可能性があります。私には複雑すぎますが。
64 ビット型の場合は、分割値をそれに変換できます。
uint64_t whole_number = higher;
whole_number <<= 32;
whole_number |= lower;
whole_number
その後、通常どおり使用できます。
差だけが必要な場合は、実際に割る前に値を減算する方が高速であることに注意してください。
どちらの値が大きいかを知っていると仮定します。
uint32_t higher1, lower1; // smaller value
uint32_t higher2, lower2; // bigger value
uint32_t del_high = higher2 - higher1;
uint32_t del_low = lower2 - lower1;
if (lower2 < lower1)
--del_high;
これで、前に説明したように結果を変換できます。または、少し運が良けれdel_high
ば0
、(差が 2^32 μs より小さい場合) になり、del_low
(μs で) 結果が得られます。
最も簡単な方法は64ビット整数型を使用することですが、これはできないと思います。32ビット整数で回答する必要があるため、マイクロ秒の上位値は999を超えることはできません。または、1000で除算した後、32ビットに収まりません。したがって、操作しているマイクロ秒の数が多いほど、999 * 2^32 + (2^32 - 1) = 4294967295999
。10進数の13桁が表示され、double
正確な除算を処理するために使用できます。
何らかの理由で32ビット整数のみを使用することを余儀なくされた場合、MichałGórnyの答えはおおよその解決策を提供します。たとえばwhole_number = 1234567890123
、結果は。になり1234567805
ます。1000で最大32ビット整数を除算することには注意が必要です。
32ビット整数で正確な答えを得る唯一の方法は、長い演算を使用することです。リマインダーを格納するために拡張できるタイプに長い数字を格納する必要があります。2つの32ビット整数を4つの16ビット桁に分割する必要があります。その後、紙のように分割することができ、リマインダーを保存するのに十分なビットがあります。のコードを参照してくださいmicro2milli
:
#include <iostream>
typedef unsigned __int32 uint32;
typedef unsigned __int64 uint64;
const uint32 MAX_INT = 0xFFFFFFFF;
uint32 micro2milli(uint32 hi, uint32 lo)
{
if (hi >= 1000)
{
throw std::runtime_error("Cannot store milliseconds in uint32!");
}
uint32 r = (lo >> 16) + (hi << 16);
uint32 ans = r / 1000;
r = ((r % 1000) << 16) + (lo & 0xFFFF);
ans = (ans << 16) + r / 1000;
return ans;
}
uint32 micro2milli_simple(uint32 hi, uint32 lo)
{
lo /= 1000;
return lo + (hi % 1000) * 4294967L;
}
void main()
{
uint64 micro = 1234567890123;
uint32 micro_high = micro >> 32;
uint32 micro_low = micro & MAX_INT;
// 1234567805
std::cout << micro2milli_simple(micro_high, micro_low) << std::endl;
// 1234567890
std::cout << micro2milli(micro_high, micro_low) << std::endl;
}
まず、2 つの変数をそれぞれ 22 の有効ビットを持つ 3 に入れます。
uint32_t x0 = l & 0x3FFFFF;
uint32_t x1 = ((l >> 22) | (h << 10)) & 0x3FFFFF;
uint32_t x2 = h >> 12;
ここで除算を行います (x ごとに 10 ビットが使用可能で、1000 < 2^10 = 1024 であるため、オーバーフローの可能性はありません)。
uint32_t t2 = x2 / 1000;
x1 |= (x2 % 1000) << 22;
uint32_t t1 = x1 / 1000;
x0 |= (x1 % 1000) << 22;
uint32_t t0 = (x0 + 500) / 1000;
/* +0 for round down, +500 for round to nearest, +999 for round up */
今、物事を元に戻します。
uint32_t r0 = t0 + t1 << 22;
uint32_t r1 = (t1 >> 10) + (t2 << 12) + (r0 < t0);
同じ手法を使用しますが、16 ビットを保持する 4 つの変数を使用すると、最大 65535 の除数に対して実行できます。その後、32 ビット演算で実行するのが難しくなります。
これに 64 ビットの int を使用できないと仮定すると、 GMP のような倍精度ライブラリを使用することをお勧めします。