【问题标题】:Floating point subtraction in C results zeroC中的浮点减法结果为零
【发布时间】:2009-09-08 06:59:02
【问题描述】:

我有用 C 语言编写的用于 16 位微控制器的代码。代码本质上做了很多浮点运算。

算术工作正常,直到结果为正,但在减法的情况下,如果预期结果为负,我得到一个零。

result = 0.005 - 0.001;      Is correctly computed as 0.004
result = 0.001 - 0.005;      Is always zero.

为什么浮动会有这样的行为?

【问题讨论】:

  • 也许你有一个unsigned float。 :)

标签: c floating-point subtraction


【解决方案1】:

有趣。它很容易成为浮点软件的故障。嵌入式系统通常包括浮点作为选项,以减少代码大小。

我不确定这是否是问题所在,因为您的第一个语句有效。

会发生什么:

result = 0.005 - 0.001;
result = -result;

result = 0.002 - 0.001;
result = 0.002 - 0.002;
result = 0.002 - 0.003;

result = 0.001 - 0.002;
result = 0.001 - 0.003;
result = 0.001 - 0.004;

这里的想法是收集有关可能导致它的有用信息,这是取证中常见的事情。这些计算的结果可能有助于确定实际问题。

根据您在 cmets 中的结果:

result = 0.005 - 0.001;  // 0.004
result = -result;        // 0.000
result = 0.002 - 0.001;  // 0.001
result = 0.002 - 0.002;  // 0.000
result = 0.002 - 0.003;  // 0.000
result = 0.001 - 0.002;  // 0.000
result = 0.001 - 0.003;  // 0.000
result = 0.001 - 0.004;  // 0.000

看起来你的浮点库有一个严重的缺点。还有两个问题:

  • 您如何打印结果(向我们展示实际代码)?
  • 您使用的是哪种微控制器和开发环境?

您的打印方式可能有问题,也可能是您的环境限制。

Ajit,我认为你真的需要给我们一些代码来帮助你。不一定是你的真实代码(你对发布真实代码的担忧被理解),只是一些说明问题的部分。

根据您的一些 cmets,即:

Adriaan,“result”的数据类型是float,即32位表示(单个)。我有CAN 作为系统接口,因此我将结果乘以 1000 以通过 CAN 总线发送它。如果它恰好是一个负数,比如 -0.003,那么我期待 CAN 消息中出现 FF FD。我没有调试器。

我不确定我是否完全理解,但我会试一试。

您有一个 32 位浮点数,例如 -0.003,然后将其乘以 1000,然后将其放入一个整数中(0xFFFD 是 -3 的 16 位二进制补码表示)。那么当你运行如下代码时会发生什么:

int main(void) {
    float w = -0.003;
    int x = (int)(w * 1000);
    int y = -3;
    int z = -32768;
    // Show us you code here for printing x, y and z.
    return 0;
}

我想让你测试一个整数的原因是它可能与浮点数完全无关。可能浮点值是完全正确的,但是您打印它的方式(CAN 方法)存在一些问题。

如果“CAN”是某种串行接口,则可能对允许通过它发送的字节有限制。我可以设想一个场景,其中高字节用作数据包标记,以便 FF 实际上可能过早地结束消息。这就是为什么我也希望你测试 -32768 (0x8000)。

很难相信STMicroelectronics 会产生如此脑残的运行时系统,以至于它无法处理负浮点数。在我看来,信息在其他地方被破坏的可能性更大(例如,“打印”过程,无论是什么)。

【讨论】:

  • 以下是观察结果:结果 = 0.005 - 0.001; = 0.004 结果 = -结果; = 0 结果 = 0.002 - 0.001; = 0.001 结果 = 0.002 - 0.002; = 0 结果 = 0.002 - 0.003; = 0 结果 = 0.001 - 0.002; = 0 结果 = 0.001 - 0.003; = 0 结果 = 0.001 - 0.004; = 0 同样结果 = 0.01 - 0.04; = 0 结果 = 0.01 - 0.05; = 0
  • Pax,ST10系列控制器。
  • 嘿,Pax,我得到了修复。正如您所说,浮点算术根本没有问题。问题在于将负浮点数转换为整数。对于 ST10,浮点 w = -0.003;整数 x = (int)(w * 1000);结果为零。正确的方法是: 1. float w = -0.003;浮动 a = -w; 2. int b = (int)(a * 1000); 3. int c = -b;因此“C”将具有对应于负浮点值的有符号整数。谢谢你的帮助 !!!!干杯:-)
  • @Ajit,这很奇怪,根本不是 C 应该做的。这几乎就像,在你的第一种情况下,它正在做 int x = (int)(w) * 1000; 给出零,因为它首先使 w 成为整数(零),然后将其乘以 1000(仍然为零)。您显示的代码应该没问题。您应该联系制作您的编译器的人并让他们知道 - 这绝对是一个错误。
【解决方案2】:

微控制器是否具有浮点硬件?可能不是;微控制器通常不会。因此,这可能是浮点运算的软件实现中的错误或限制。查找文档,和/或阅读源代码(如果有)。

【讨论】:

  • 不,微型没有浮点硬件。但相反,它有浮点库。从以前的一些项目来看,.lib 中没有错误。
【解决方案3】:

您是否有可能尝试将 0.001 - 0.005 的结果打印为 5 个字符的字段?如果是这样,结果将显示为四舍五入到 0.0。

【讨论】:

  • 谢谢斯蒂芬,但我将结果乘以 1000 以将其视为整数。
【解决方案4】:

你能提供更多的上下文吗?在此示例中,评估可能由编译器完成,因为这两个常量在编译时都是已知的。 “结果”的类型是什么? (即IEEE-754 half or single or double?)你如何评价这个?使用调试器、if 语句还是 printf?我问这个的原因是因为可能有一个红鲱鱼。例如,如果您使用 printf 格式不正确,您可能只是看不到减号。当您查看二进制表示(即 printf("%lx", result) 如果它是 32 位)并检查符号位时。

【讨论】:

  • Adriaan,“结果”的数据类型是浮点数,即 32 位表示(单个)。我将 CAN 作为系统接口,因此我将结果乘以 1000 以通过 CAN 发送 ti。如果它恰好是一个负数,比如 -0.003,那么我期待 CAN 消息中出现 FF FD。我没有调试器。
【解决方案5】:

这不是天生的 C 东西,所以归根结底是你没有说的话:-

  • 控制器(显然是 ST10)
  • 编译器(Cosmic 软件?如果符合标准)
  • 浮点库(来自编译器?)
  • 程序(演示它的最短的完整程序会很好。)

可能是您的演员从浮点转换为您未显示的 int 吗?

【讨论】:

  • 看起来不像,因为相同的代码在结果为正时有效。由于 IP 问题,我无法与您共享该程序。对此感到抱歉。
  • 你当然可以做一个简短的形式吗?我觉得代码的其他部分更有可能是问题所在。毕竟,您对“结果”中的内容的了解只是推断出来的。
  • int x = (int)(w * 1000);结果是 Zero Try 1000.0 它正在执行隐式转换,将 1000 视为 const int。
猜你喜欢
  • 2016-05-02
  • 1970-01-01
  • 1970-01-01
  • 2012-12-10
  • 2015-11-28
  • 1970-01-01
  • 2015-03-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多