在 32 位机器上使用 48 位

Working with 48bit on 32bit machine

我这里有一个小的 "PLC",在 32 位 MCU 上 运行。 我可以定义的最大变量是 uint32(unsigned int)。 我正在读取一个仪表,它以 6 个字符(通过串行线)为我提供 48 位值。 我现在通过移动字符将这个值存储在两个 32 位中。

T1Low = (unsigned int) mybuffer[3] << 24 | (unsigned int) mybuffer[2] << 16 |
        (unsigned int) mybuffer[1] <<  8 | (unsigned int) mybuffer[0];
T1High = (unsigned int) mybuffer[5] << 8 | (unsigned int) mybuffer[4];

但是我想把48bit的值除以1000再存为一个32bit(因为除完之后精度够了)

理论上是这样的

unsigned int result = (T1High << 32 | T1Low) / 1000;

当然这行不通因为偏移太大... 有什么办法吗?

编辑:决议说明: 仪表以 kWh 为单位测量,但值以 Wh 为单位给出。最大值为 9 999 999,足以保存在 32 位中。但是从仪表上我会得到 9 999 999 000 ...所以我需要在最后切掉 3 个零..

EDIT2:cmaster 给出了正确的答案:) 如果有人想把代码只写成2行,那么:

unsigned int value1;
unsigned int value2;
unsigned int value3;

value2 += (value1%1000) << 16;
unsigned long result = ((value2 / 1000) << 16) | ((value3 + ((value2 % 1000) << 16)) / 1000);

从数学上讲,T1High << 32 | T1Low 等价于

T1High * 2**32 + T1Low

和的除法可以用除法的和代替,可以得到以下公式:

((T1High * 2**16 * 2**16) + T1Low) / 1000
(T1High * 2**16 / 1000 * 2**16) + (T1Low / 1000)
(((T1High << 16) / 1000) << 16) + (T1Low / 1000)

不过,这可能会导致精度下降:

4823248397 = ((1123 << 32) + 123899) / 1000
4823187579 = (((1123 << 16) / 1000) << 16) + (123899 /1000)

我会以 2^16 为基数进行除法:将您的值分成三个 16 位块,将它们存储在 uint32_t 变量中,然后执行以下操作:

uint32_t result1 = value1/1000;
uint32_t remainder = value1 - result1*1000;
value2 += remainder << 16;
uint32_t result2 = value2/1000;
remainder = value2 - result2*1000;
value3 += remainder << 16;
uint32_t result3 = value3/1000;

当然,您也可以将其表述为循环,但我怀疑这样做是否值得。这种方法尽可能精确。缺点是需要3次除法和2次乘法,如果你的计算资源紧张,可能会导致性能问题。

But I would like to divide the 48bit value by 1000 and save it to one 32bit

您的规范没有意义。 2^48 = 2.81e142.81e14 / 1000 = 2.81e11。一个 32 位数字可以容纳 4.29e9,所以除以 1000 不能解决任何问题 - 这个数字不合适。

也就是说,不要除以 1000,除以 1024。这与将数字右移 10 相同。或者,如果您愿意,则丢弃 10 个最低有效位。