【问题标题】:Working with 48bit on 32bit machine在 32 位机器上使用 48 位
【发布时间】:2017-09-01 15:15:38
【问题描述】:

我这里有一个小型“PLC”,它在 32 位 MCU 上运行。 我可以定义的最大变量是 uint32(无符号整数)。 我正在读一米,它给我 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);

【问题讨论】:

  • 您需要查找“多精度算术”或相关内容。是的,有办法做到这一点。完整的通用解决方案(例如GMP — GNU Multi-Precision)可能有点矫枉过正,但你最终会使用函数来完成这项工作,而且它们不会是微不足道的。
  • 无符号整数结果 = (T1High > 10)
  • KonstantinL:我会丢失太多精度。我只需要从数字中删除 3 个零...

标签: c 32bit-64bit 32-bit


【解决方案1】:

从数学上讲,T1High &lt;&lt; 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】:

我会以 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;

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

【讨论】:

  • 我试过这个方法。我在最大数字 9 999 999 000 上尝试过,我想要 9 999 999。所以我将数字拆分为 3 个 16 位(0xE018、0x540B、0x0002)。我得到 0x00、0x98、0x7f
  • @Daniel 似乎您使用了错误的字节顺序:0x0002 应该是 value10xe018 应该是 value3
  • @Daniel 想想你会如何用笔和纸来划分。
  • 感谢您的回复。我也尝试切换顺序,我得到了 0x39、0x5E4A、0xC353。编辑:看起来我做对了,但我把它储存得很糟糕。现在我在 repl.it 上尝试它时它给了我正确的:0x0、0x98 和 0x967f :) 我将在星期一再次测试它,我会出局。
  • 还有可能的小优化:使用模而不是“手动”计算余数,理论上我不需要存储第一个结果:) value2 += (value1%1000) &lt;&lt; 16; unsigned long result2 = value2/1000; value3 += (value2%1000) &lt;&lt; 16; unsigned long result3 = value3/1000;
【解决方案3】:

但我想将 48 位的值除以 1000 并保存到一个 32 位

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

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

【讨论】:

  • 我知道这听起来很奇怪,但原因很简单:仪表以 kWh 为单位,但值以 Wh 为单位。最大值为 9 999 999,足以保存在 32 位中。但是从仪表上我会得到 9 999 999 000...所以我需要在最后切掉 3 个零...
猜你喜欢
  • 2011-08-08
  • 2014-12-16
  • 1970-01-01
  • 1970-01-01
  • 2011-02-11
  • 2018-04-28
  • 2011-04-09
  • 2017-03-11
  • 2021-10-18
相关资源
最近更新 更多