【问题标题】:Embedded division with negative numbers带有负数的嵌入式除法
【发布时间】:2015-12-11 19:42:01
【问题描述】:

在我从事嵌入式编程的所有岁月中,我通常从不需要使用负数。这很疯狂,但他们只是在我的工作中不经常出现。

我目前正在处理一个传感器读数,它可以是正数或负数,需要按 0.006 缩放,保留符号。为了避免在运行时进行不必要的浮点计算,我有一个算法可以将其转换为分子和分母 (3/500)。正数情况下一切正常,但负数情况如下:

Raw data:         -103
Multiplied by 3:  -309
Divided by 500:   36893488147419102

我知道这个数字是从哪里来的,我有一个解决方法,但我宁愿相信数学就是数学。

这是相同的十六进制计算:

Raw data:         0xFFFFFFFFFFFFFF99
Multiplied by 3:  0xFFFFFFFFFFFFFECB
Divided by 500:   0x0083126E978D4FDE

在计算器中 (SpeedCrunch):

0xFFFFFFFFFFFFFECB/500 = 0x83126E978D4FDE.9D2F1A9FBE76C8B44

原来的 36893488147419102 是 SpeedCrunch 结果的组成部分 0x83126E978D4FDE

我不想保存符号,做一个正除法,然后在每次用负数除法时重新添加符号。这是怎么回事?

环境是CortexM3 micro,GCC4.9.3,使用c++11。计算是在int64_t 上完成的,分子/分母是uint64_t

编辑: 下面是响应 Michael 评论的代码 sn-p:

int64_t data = -103;
uint64_t resolutionNumerator = 3;
uint64_t resolutionDenominator = 500;

data *= resolutionNumerator;
data /= resolutionDenominator;

【问题讨论】:

  • 我知道你说“计算是在……上完成的”,但为了消除任何猜测,你应该真正显示计算中涉及的表达式的实际来源以及类型对于涉及的任何变量。
  • 所有这些数字都基于来自其他地方的表格和原始数据。这是非常通用的代码,在任何地方都没有“幻数”。但是,我将其浓缩并添加了一个sn-p。
  • 具有混合数据类型的表达式通常是一个非常糟糕的主意。您应该始终争取类型一致,如果这是不切实际的(在这种情况下不是),您应该使用显式转换来表明类型不一致是故意的。该语言定义了可能无法满足您期望或需要的隐式转换和类型提升(如本例所示)。
  • 您的传感器似乎不太可能以保证 64 位的分辨率提供数据,并且在任何情况下除以 0.0006 都会丢弃超过 10 位的信息。您确定需要 64 位还是这是执行缩放的正确位置?您通常只会缩放数据以以“真实世界”单位表示。内部计算最好在传感器数据的全分辨率下执行。
  • 是的,你是对的。这个传感器只有 16 位。但是,这是非常共享的代码,不同的传感器可以提供高达 64 位的数据。每个传感器读数都有一个配置,可以通过乘以 10 或 100 等来获得更高的分辨率,其唯一目的是避免浮点数学和转换为各个应用程序关心的“真实世界”单位。

标签: embedded division negative-number


【解决方案1】:

具有相同宽度的有符号和无符号整数的运算,例如uint64_tint64_t,会导致有符号操作数被转换为无符号操作数的类型。

因此这两个表达式是等价的:

  • (int64_t) -103 * (uint64_t) 3 / (uint64_t) 500
  • (uint64_t) -103 * (uint64_t) 3 / (uint64_t) 500

如果分子和分母使用有符号类型int64_t,则结果将保留符号。

【讨论】:

  • 将分子和分母切换为uint32_t,我的所有单元测试都通过了。谢谢!
【解决方案2】:

这是怎么回事?

当您将其从无符号类型转换为有符号类型时,前导 1 位保留用于来自 2 的补码表示的负数。

可能可以通过将最终结果转换回有符号值来恢复预期结果。作为有符号值,前 1 位表示负值。


我们只能说可能,因为我们还没有看到套路。 Michael 在评论中指出了同样的问题。

int64_t data = -103;
uint64_t resolutionNumerator = 3;
uint64_t resolutionDenominator = 500;

C/C++ 的整体提升规则是指将有符号类型提升为无符号类型。这就是为什么-1 > 1

int i = -1;
unsigned int j = i;

if ( i > j )
    printf("-1 is greater than 1\n");

您没有执行上述回复中提到的演员表;但是编译器确实执行了积分提升。对于你所观察到的,它的区别是一样的。

【讨论】:

  • 0x0083126E978D4FDE(结果)没有前导 1 位。铸造没有帮助。这里没有自定义数学例程。只需乘除。
  • 是的,浮点问题与问题几乎无关。这与浮点无关。顺便说一句,浮点数学(软件实现)在我的目标上的成本是积分数学的 24 倍。由于这段代码每秒运行 250 次,我有动力避免使用浮点数。
  • 太糟糕了?首先,我在发布之前实施了修复此问题的“黑客”。问题是回答为什么简单的除法不起作用。其次,为什么任何人都必须编写自定义例程来处理除法负数?数学就是数学。我愿意相信它。
  • 是的,很抱歉语气。我试图编辑评论,但有 5 分钟的限制。我不是故意的。 -1 > 1 这件事肯定令人困惑。感谢您的帮助。
  • Aaa现在整个评论线程都参考了您编辑的内容。哦,好吧,你的答案现在更重要了。
猜你喜欢
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 2022-07-28
  • 2017-07-28
  • 2016-02-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多