3

我有一个大数字,时间(微秒)存储在两个 32 位变量中。我需要帮助,如何将微秒时间更改为毫秒,以便我可以存储 32 位数字的差异结果。

更多细节:我有一次在两个 32 位变量中。其中一个变量的有效位较高,而另一个变量的有效位较低。这次有微秒的分辨率,所以我想把它改成毫秒。那么如何划分存储在两个变量中的数字。

4

4 回答 4

6

如果你没有 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 + 2ms 的错误(除非我遗漏了什么)。


如果你真的想要更好的精度并且不关心效率,你可以在中间使用一点浮点运算,并将结果正确四舍五入。我怀疑这是否值得努力:

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 为单位)的结果。

于 2012-08-25T08:47:59.177 回答
1

最简单的方法是使用 64 位整数类型,但我假设你不能这样做。由于您希望您的答案是 32 位整数,因此微秒的高阶值不能大于 999,否则除以 1000 后将不适合 32 位。因此,您使用的微秒数越大999 * 2^32 + (2^32 - 1) = 4294967295999. 它为您提供 13 位十进制数字,您可以使用它double来处理精确除法。

如果由于某种原因您被迫仅使用 32 位整数,那么 Michał Górny 的答案为您提供了一个近似解决方案。例如,whole_number = 1234567890123它会给出1234567805. 因为在 1000 上除以 max 32-bit int 有一个提醒。

使用 32 位整数获得准确答案的唯一方法是使用长算法。它需要将长数字存储在可以扩展以存储提醒的类型中。您必须将两个 32 位整数拆分为四个 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;
}
于 2012-08-25T08:54:22.037 回答
1

首先,将您的两个变量放入 3 中,每个变量有 22 个有效位。

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 位,您可以对高达 65535 的除数执行此操作。然后使用 32 位算术来执行此操作变得更加困难。

于 2012-08-28T09:43:03.370 回答
0

假设您不能为此使用 64 位 int,我建议您使用多精度库,例如 GMP

于 2012-08-25T08:45:43.997 回答