You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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;
}

问题原因分析

  1. 定点数格式误解:q_format_voltaged的int值15是Q16格式的编码值,对应的实际浮点电压是15 / 65536 ≈ 0.000229V,并非你以为的15V。这个极小值减去1.5V的offset,结果自然接近-1.5V。
  2. 整数未转定点格式参与运算: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 07:10:53