为何C代码中'q_format_voltaged'值未在'q_format_voltages'运算中生效?
问题描述
调试一段定点Q16格式的C代码时遇到问题:在main函数的adc == 1分支中,计算得到q_format_voltaged的int值为15,但执行q_format_voltages = q_format_voltaged - q_format_voltage_offset后,结果约为-1.5,看起来q_format_voltaged像是被当作0处理。相关代码如下:
#include <stdio.h> // Define Q format parameters #define FIXED_POINT_FRACTIONAL_BITS 16 int float_to_Q(float x) { return (int)(x * (1 << FIXED_POINT_FRACTIONAL_BITS)); // Multiply by 2^Q to shift the decimal point } float Q_to_float(int x) { return (float)x / (1 << FIXED_POINT_FRACTIONAL_BITS); // Divide by 2^Q to get the float value } int fixed_point_multiply(int x, int y) { long long result = (long long)x * y; // Perform multiplication in a wider type to avoid overflow // Adjust the result for the Q format representation result >>= FIXED_POINT_FRACTIONAL_BITS; return (int)result; } int fixed_point_divide(int x, int y) { long long result = (long long)x * (1 << FIXED_POINT_FRACTIONAL_BITS); // Multiply numerator by 2^Q result /= y; // Perform division return (int)result; } float original_voltage = 5; // Define voltage as a global variable float original_LPF = 0; float original_LPF_A = 0.002; float original_due_volt = 3; float original_ADC_bits = 2; float original_voltage_offset = 1.5; volatile int DUTYCYCLE = 10; int adc = 1; int mode = 1; int main() { int q_format_voltage = float_to_Q(original_voltage); int q_format_LPF = float_to_Q(original_LPF); int q_format_LPF_A = float_to_Q(original_LPF_A); int q_format_due_volt = float_to_Q(original_due_volt); int q_format_ADC_bits = float_to_Q(original_ADC_bits); int q_format_voltage_offset = float_to_Q(original_voltage_offset); int q_format_voltage_squared; int q_format_voltagem; int q_format_voltaged; int q_format_voltages; if (adc == 1) { q_format_voltagem = fixed_point_multiply(DUTYCYCLE, q_format_due_volt); q_format_voltaged = fixed_point_divide(q_format_voltagem, q_format_ADC_bits); if (mode == 1) { q_format_voltages = q_format_voltaged - q_format_voltage_offset; q_format_voltage_squared = fixed_point_multiply(q_format_voltages, q_format_voltages); // Square voltages } q_format_LPF = q_format_LPF - fixed_point_multiply(q_format_LPF_A, (q_format_LPF - q_format_voltage_squared)); // LPF calculation in Q16 format } // Printing results printf("Original voltage: %f\n", original_voltage); printf("Q format voltage: %d\n", q_format_voltage); printf("Original LPF: %f\n", original_LPF); printf("Q format LPF: %d\n", q_format_LPF); printf("Original LPF_A: %f\n", original_LPF_A); printf("Q format LPF_A: %d\n", q_format_LPF_A); printf("Original Due volt: %f\n", original_due_volt); printf("Q format Due volt: %d\n", q_format_due_volt); printf("Original ADC bits: %f\n", original_ADC_bits); printf("Q format ADC bits: %d\n", q_format_ADC_bits); printf("Original voltage offset: %f\n", original_voltage_offset); printf("Q format voltage offset: %d\n", q_format_voltage_offset); printf("dutycycle * due volt: %d\n", q_format_voltagem); printf("voltagem divided by adc bits: %d\n", q_format_voltaged); printf("voltage - voltage offset: %f\n", Q_to_float(q_format_voltages)); printf("voltages number squared: %f\n", Q_to_float(q_format_voltage_squared)); printf("LPF amount: %f\n", Q_to_float(q_format_LPF)); return 0; }
问题原因分析
- 定点数格式误解:
q_format_voltaged的int值15是Q16格式的编码值,对应的实际浮点电压是15 / 65536 ≈ 0.000229V,并非你以为的15V。这个极小值减去1.5V的offset,结果自然接近-1.5V。 - 整数未转定点格式参与运算:
fixed_point_multiply函数设计用于计算两个Q16格式定点数的乘积,运算后需右移16位修正格式。但你直接传入了整数DUTYCYCLE=10(属于Q0格式,无小数位),导致乘法结果被错误右移16位:- 正确逻辑中,
DUTYCYCLE=10作为Q16格式时编码值应为10 * 65536 = 655360,但直接传10会导致fixed_point_multiply(10, 196608)(q_format_due_volt=3*65536=196608)的结果为(10*196608)>>16 = 30,对应实际值30/65536≈0.000458V,后续除法得到的q_format_voltaged=15自然是极小值。
- 正确逻辑中,
修复方案
方案1:将整数DUTYCYCLE转换为Q16格式后参与运算
修改fixed_point_multiply的调用,先把DUTYCYCLE转成Q16格式:
q_format_voltagem = fixed_point_multiply(float_to_Q((float)DUTYCYCLE), q_format_due_volt);
或者直接计算Q16格式的DUTYCYCLE:
q_format_voltagem = fixed_point_multiply(DUTYCYCLE * (1 << FIXED_POINT_FRACTIONAL_BITS), q_format_due_volt);
方案2:新增整数与定点数的乘法函数
如果需要频繁处理整数和定点数的乘法,可以单独写一个函数避免格式混淆:
int fixed_point_multiply_int(int integer, int q_num) { // 整数(Q0)乘Qn数,结果直接是Qn格式,无需额外右移 return (long long)integer * q_num; }
然后调用这个函数替代原来的fixed_point_multiply:
q_format_voltagem = fixed_point_multiply_int(DUTYCYCLE, q_format_due_volt);
修改后的完整代码(以方案1为例)
#include <stdio.h> // Define Q format parameters #define FIXED_POINT_FRACTIONAL_BITS 16 int float_to_Q(float x) { return (int)(x * (1 << FIXED_POINT_FRACTIONAL_BITS)); // Multiply by 2^Q to shift the decimal point } float Q_to_float(int x) { return (float)x / (1 << FIXED_POINT_FRACTIONAL_BITS); // Divide by 2^Q to get the float value } int fixed_point_multiply(int x, int y) { long long result = (long long)x * y; // Perform multiplication in a wider type to avoid overflow // Adjust the result for the Q format representation result >>= FIXED_POINT_FRACTIONAL_BITS; return (int)result; } int fixed_point_divide(int x, int y) { long long result = (long long)x * (1 << FIXED_POINT_FRACTIONAL_BITS); // Multiply numerator by 2^Q result /= y; // Perform division return (int)result; } float original_voltage = 5; // Define voltage as a global variable float original_LPF = 0; float original_LPF_A = 0.002; float original_due_volt = 3; float original_ADC_bits = 2; float original_voltage_offset = 1.5; volatile int DUTYCYCLE = 10; int adc = 1; int mode = 1; int main() { int q_format_voltage = float_to_Q(original_voltage); int q_format_LPF = float_to_Q(original_LPF); int q_format_LPF_A = float_to_Q(original_LPF_A); int q_format_due_volt = float_to_Q(original_due_volt); int q_format_ADC_bits = float_to_Q(original_ADC_bits); int q_format_voltage_offset = float_to_Q(original_voltage_offset); int q_format_voltage_squared; int q_format_voltagem; int q_format_voltaged; int q_format_voltages; if (adc == 1) { // 修复:将DUTYCYCLE转换为Q16格式后再相乘 q_format_voltagem = fixed_point_multiply(float_to_Q((float)DUTYCYCLE), q_format_due_volt); q_format_voltaged = fixed_point_divide(q_format_voltagem, q_format_ADC_bits); if (mode == 1) { q_format_voltages = q_format_voltaged - q_format_voltage_offset; q_format_voltage_squared = fixed_point_multiply(q_format_voltages, q_format_voltages); // Square voltages } q_format_LPF = q_format_LPF - fixed_point_multiply(q_format_LPF_A, (q_format_LPF - q_format_voltage_squared)); // LPF calculation in Q16 format } // Printing results printf("Original voltage: %f\n", original_voltage); printf("Q format voltage: %d\n", q_format_voltage); printf("Original LPF: %f\n", original_LPF); printf("Q format LPF: %d\n", q_format_LPF); printf("Original LPF_A: %f\n", original_LPF_A); printf("Q format LPF_A: %d\n", q_format_LPF_A); printf("Original Due volt: %f\n", original_due_volt); printf("Q format Due volt: %d\n", q_format_due_volt); printf("Original ADC bits: %f\n", original_ADC_bits); printf("Q format ADC bits: %d\n", q_format_ADC_bits); printf("Original voltage offset: %f\n", original_voltage_offset); printf("Q format voltage offset: %d\n", q_format_voltage_offset); printf("dutycycle * due volt: %d\n", q_format_voltagem); printf("voltagem divided by adc bits: %d\n", q_format_voltaged); printf("voltage - voltage offset: %f\n", Q_to_float(q_format_voltages)); printf("voltages number squared: %f\n", Q_to_float(q_format_voltage_squared)); printf("LPF amount: %f\n", Q_to_float(q_format_LPF)); return 0; }
修复后,q_format_voltaged对应的实际电压为(10*3)/2=15V,转成Q16格式是15*65536=983040,减去q_format_voltage_offset=98304后,q_format_voltages=884736,转成float是884736/65536=13.5V,符合预期。
内容的提问来源于stack exchange,提问作者FIG
相关产品推荐
相关产品推荐

