在 C 中使用浮点数除法 with/without

Dividing with/without using floats in C

下面是我用 C 语言编写的主要函数(用于 PIC18F8722 微处理器)试图以由 unsigned int 函数 get_ADC_value() 设置的特定频率驱动 2 个多路复用 7 段显示器。显示屏还显示当前的多路复用频率。此频率范围由 #define 设置在 LAB_Fmin 和 LAB_Fmax 范围内,并且必须随着 get_ADC_value() 从 0 到 255 的增加或减少而缩放。

但是此代码不起作用,因为我认为在 freq = 处存在从 intfloat 的隐式转换。

挑战在于使用浮点数修复此错误并找到仅使用整数类型的替代方法(intchar...)。

 while (1) {

   unsigned int x, y, z;
   float freq, delay;

    x = get_ADC_value(); 
    y = x & 0b00001111;
    z = (x & 0b11110000) >> 4 ;

    freq = LAB_Fmin + (((LAB_Fmax) - (LAB_Fmin))/ 255)*x ;
    delay = 1/(freq*1000); // convert hZ to ms delay accurately

    LATF = int_to_SSD(y); 
    LATH = 0b11111110; //enable 7seg U1
    for (unsigned int i = 0; i<(delay) ; i++){ 
        Delay10TCYx(250); //1ms delay
    }

    LATF = int_to_SSD(z); 
    LATH = 0b11111101; //enable 7seg U2
    for (unsigned int j = 0; j<(delay) ; j++){
        Delay10TCYx(250); //1ms delay
    }
}

你对整数除法的看法是正确的。 更改为

freq = LAB_Fmin + (((LAB_Fmax) - (LAB_Fmin)) / 255.0)*x;
                                                  ^^ 
 freq = LAB_Fmin + (((LAB_Fmax) - (LAB_Fmin))/ 255)*x ;

这确实是对整数的隐式转换,而您正在执行整数除法。

那是因为 255 是整数文字。

将其更改为 255.0 作为双字面值,这应该能很好地配合您的计算。

如果你想更精确,你甚至可以使用浮点文字,如 255.0f 或显式转换,如 (float)255.

您的代码可能如下所示:

freq = LAB_Fmin + (((LAB_Fmax) - (LAB_Fmin))/ 255.0)*x ;

或者这样:

freq = LAB_Fmin + (((LAB_Fmax) - (LAB_Fmin))/ (float)255)*x ;

C 被定义为使用整数除法来划分 ints,并且只有当有浮点数时才会先 "promote" 其他 ints 到 floats。请注意,如果将其分配给 float,甚至会发生这种情况 - 如果右侧全是 int,则除法将全部为整数,并且只有在最终分配时才会将 C 转换int 结果为 float.

所以,用你的台词:

freq = LAB_Fmin + (((LAB_Fmax) - (LAB_Fmin))/ 255)*x ;

这完全取决于 LAB_FmaxLAB_Fmin 是什么。 freqx 是什么并不重要,因为 "damage" 已经完成了,因为括号强制除法是第一个。

如果那些 LAB_F 变量是 ints,使用浮点除法的最简单方法是通过使常量 255 成为浮点来简单地告诉 C 你想要那个数字而不是整数,通过使用小数点:255.(或 255.0 不那么微妙)。

如果你只想使用整数运算,那么通常的建议是先做所有的乘法再做除法。当然,这有中间结果溢出的风险 - 为了帮助 that,您可以使用类型 long。将 LAB_Fx 变量定义为 long,最后进行除法:

freq = LAB_Fmin + (((LAB_Fmax) - (LAB_Fmin)) * x / 255);

默认情况下,整数的数学运算结果也会变成整数, 所以你需要将其中一个文字表达为 double/float

freq = LAB_Fmin + (((LAB_Fmax) - (LAB_Fmin))/ 255.0)*x ;

或投(float)

与许多其他州一样,第一个选项是最常实施的。

代码审查:

  • unsigned int x, y, z; 避免在嵌入式系统上使用原始整数类型。应始终使用 stdint.h 中的精确宽度类型,以便您确切知道您使用的尺寸。如果您无权访问 stdint.h,那么您自己输入这些类型。

  • float freq, delay; 大多数嵌入式系统通常应避免使用浮点数。特别是在没有 FPU 的 8 位 MCU 上!这将导致软件定义的浮点数非常慢且耗费内存。你似乎没有理由在这个程序中使用浮点数,看起来你应该能够用 uint16_t 或更小的值来编写这个算法,除非你有极端的精度要求。

  • x = get_ADC_value();既然您似乎只对 ADC 读取的 8 位感兴趣,为什么不使用 8 位类型呢?

  • 请注意,二进制数字文字不是标准 C。

  • ((LAB_Fmax) - (LAB_Fmin))/ 255 这看起来很可疑。首先,这些是整数还是浮点数?它们的大小是多少?您的问题的答案取决于此。通过将文字交换为 255.0f,您可以强制转换为浮动。但是你确定除法应该是 255?而不是 256?

  • i<(delay)。您应该始终避免在循环条件内使用浮点表达式,因为它会使循环不必要地变慢,并可能导致浮点不准确的错误。此外,括号没有任何意义。

总的来说,您的程序存在 "sloppy typing" 问题,这意味着程序员没有考虑每个表达式中使用的类型。请注意,文字也有类型。隐式转换可能会导致很多这些表达式在太大的类型上计算,这对 PIC 来说是个坏消息。我建议阅读 "balancing",也就是 通常的算术转换

这 "sloppy typing" 将导致您的程序变得非常臃肿和缓慢,而没有任何收获。您必须记住,PIC 可能是目前仍在生产的代码效率最低的 MCU。为任何 8 位 MCU 编写 C 代码时,应避免使用大于 8 位的类型。特别是,你应该避免像瘟疫一样的 32 位整数和浮点数。

您的程序将所有数据重新缩放为便于程序员思考的类型。这是一个常见的设计错误——您的程序应该使用易于处理器使用的类型。例如,您可以使用计时器滴答作为单位而不是毫秒。