在 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 =
处存在从 int
到 float
的隐式转换。
挑战在于使用浮点数修复此错误并找到仅使用整数类型的替代方法(int
、char
...)。
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 被定义为使用整数除法来划分 int
s,并且只有当有浮点数时才会先 "promote" 其他 int
s 到 float
s。请注意,如果将其分配给 float
,甚至会发生这种情况 - 如果右侧全是 int
,则除法将全部为整数,并且只有在最终分配时才会将 C 转换int
结果为 float
.
所以,用你的台词:
freq = LAB_Fmin + (((LAB_Fmax) - (LAB_Fmin))/ 255)*x ;
这完全取决于 LAB_Fmax
和 LAB_Fmin
是什么。 freq
或 x
是什么并不重要,因为 "damage" 已经完成了,因为括号强制除法是第一个。
如果那些 LAB_F
变量是 int
s,使用浮点除法的最简单方法是通过使常量 255
成为浮点来简单地告诉 C 你想要那个数字而不是整数,通过使用小数点:255.
(或 255.0
不那么微妙)。
如果你只想使用整数运算,那么通常的建议是先做所有的乘法再做除法。当然,这有中间结果溢出的风险 - 为了帮助 that,您可以使用类型 long
。将 LAB_F
或 x
变量定义为 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 位整数和浮点数。
您的程序将所有数据重新缩放为便于程序员思考的类型。这是一个常见的设计错误——您的程序应该使用易于处理器使用的类型。例如,您可以使用计时器滴答作为单位而不是毫秒。
下面是我用 C 语言编写的主要函数(用于 PIC18F8722 微处理器)试图以由 unsigned int 函数 get_ADC_value()
设置的特定频率驱动 2 个多路复用 7 段显示器。显示屏还显示当前的多路复用频率。此频率范围由 #define
设置在 LAB_Fmin 和 LAB_Fmax 范围内,并且必须随着 get_ADC_value()
从 0 到 255 的增加或减少而缩放。
但是此代码不起作用,因为我认为在 freq =
处存在从 int
到 float
的隐式转换。
挑战在于使用浮点数修复此错误并找到仅使用整数类型的替代方法(int
、char
...)。
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 被定义为使用整数除法来划分 int
s,并且只有当有浮点数时才会先 "promote" 其他 int
s 到 float
s。请注意,如果将其分配给 float
,甚至会发生这种情况 - 如果右侧全是 int
,则除法将全部为整数,并且只有在最终分配时才会将 C 转换int
结果为 float
.
所以,用你的台词:
freq = LAB_Fmin + (((LAB_Fmax) - (LAB_Fmin))/ 255)*x ;
这完全取决于 LAB_Fmax
和 LAB_Fmin
是什么。 freq
或 x
是什么并不重要,因为 "damage" 已经完成了,因为括号强制除法是第一个。
如果那些 LAB_F
变量是 int
s,使用浮点除法的最简单方法是通过使常量 255
成为浮点来简单地告诉 C 你想要那个数字而不是整数,通过使用小数点:255.
(或 255.0
不那么微妙)。
如果你只想使用整数运算,那么通常的建议是先做所有的乘法再做除法。当然,这有中间结果溢出的风险 - 为了帮助 that,您可以使用类型 long
。将 LAB_F
或 x
变量定义为 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 位整数和浮点数。
您的程序将所有数据重新缩放为便于程序员思考的类型。这是一个常见的设计错误——您的程序应该使用易于处理器使用的类型。例如,您可以使用计时器滴答作为单位而不是毫秒。