将 2x32 位大整数除以 1000

问题描述 投票:0回答:5

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

更多详情: 我有一次有两个 32 位变量。其中一个变量具有较高有效位,而其他变量具有较低有效位。这次有微秒分辨率,所以我想将其更改为毫秒。那么如何将存储在两个变量中的数字相除。

c++ algorithm bignum
5个回答
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 + 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 为单位)内得到结果。


1
投票

最简单的方法是使用 64 位整数类型,但我假设你不能这样做。由于您希望答案为 32 位整数,因此微秒的高位值不能大于 999,否则除以 1000 后将不适合 32 位。因此,您使用的较大微秒数是

999 * 2^32 + (2^32 - 1) = 4294967295999
。它为您提供 13 位十进制数字,您可以使用
double
来处理精确除法。

如果您由于某种原因被迫仅使用 32 位整数,Michał Górny 的答案为您提供了一个“近似”解决方案。例如。对于 whole_number = 1234567890123 它将给出

1234567805
的结果。因为最大 32 位 int 除以 1000 有一个提醒。

获得 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; }



1
投票

uint32_t x0 = l & 0x3FFFFF; uint32_t x1 = ((l >> 22) | (h << 10)) & 0x3FFFFF; uint32_t x2 = h >> 12;

现在进行除法(每个 x 有 10 个可用位?,以及 1000 

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 */ < 2^10 = 1024 so there is no overflow possible)

现在把东西放回原处。

uint32_t r0 = t0 + t1 << 22; uint32_t r1 = (t1 >> 10) + (t2 << 12) + (r0 < t0);

使用相同的技术,但使用四个保存 16 位的变量,您可以对除数高达 65535 执行此操作。然后使用 32 位算术执行此操作就变得更困难。


0
投票
多精度库,例如 GMP


0
投票

/* return 64 bit x / 1000 faster than the normal gcc implementation using by about 3x With thanks to https://0x414b.com/2021/04/16/arm-division.html and https://stackoverflow.com/questions/74765410/multiply-two-uint64-ts-and-store-result-to-uint64-t-doesnt-seem-to-work */ static inline uint64_t uint64_div1000(uint64_t x) { x >>= 3U; uint64_t a_lo = (uint32_t)x; uint64_t a_hi = x >> 32; const uint64_t b_lo = 0xe353f7cfU; const uint64_t b_hi = 0x20c49ba5U; uint64_t a_x_b_hi = a_hi * b_hi; uint64_t a_x_b_mid = a_hi * b_lo; uint64_t b_x_a_mid = b_hi * a_lo; uint32_t a_x_b_lo = (a_lo * b_lo)>>32; // 64-bit product + two 32-bit values uint64_t middle = a_x_b_mid + a_x_b_lo + (uint32_t)b_x_a_mid; // 64-bit product + two 32-bit values uint64_t r = a_x_b_hi + (middle >> 32) + (b_x_a_mid >> 32); return r >> 4U; }

© www.soinside.com 2019 - 2024. All rights reserved.