0

I'm having some problems and getting confused about the proper way of doing some operations with uint16_t and double for C embedded software (Using GCC).

Ex1:

uint16_t PD_input =723;// Input will always be greater than output
uint16_t PD_output =246;
uint16_t FQ_u16 = PD_input/PD_output;

This will result in: FQ_u16=2, no?

Ex2:

uint16_t PD_input =723;
uint16_t PD_output =246;
double FQ_dbl = PD_input/PD_output;  

In this case FQ_dbl =2.9390, but I get 0.... or should I do:

double FQ_dbl = (double)(PD_input/PD_output);  

In this case I don't know if doinng the casting will cause some problems. I get 0 anyway.

If I do the casting on the other side the next example will be correct, or how should I do it? Ex3:

double FQ_dbl = PD_input/PD_output; 
uint16_t var= (uint16_t)FQ_dbl; //Is this correct?

What is the proper way of dividing two uint16_t? What is the proper way of casting/convert a double to an uint16_t?

Ex4:

uint16_t var=7342;
double target = var/1000; 

target=7.342?-->I get 0

EDIT: I try this example using UNITY (It uses GCC)

uint16_t Operations(void)
{
    uint16_t PD_input=723;
    uint16_t PD_output=246;
    uint16_t FQ_u16_raw=PD_input/PD_output;
    uint16_t FQ_u16 = (PD_input + (PD_output >> 1)) / PD_output;
    printf("FQ_u16: %d, FQ_u16_raw: %d",FQ_u16,FQ_u16_raw);

    double FQ_dbl=(PD_input/PD_output);
    printf("\nFQ_dbl: %d, FQ_dbl:%f",FQ_dbl,FQ_dbl);

    FQ_dbl=(double)(PD_input/PD_output);
    printf("\nFQ_dbl: %d, FQ_dbl:%f",FQ_dbl,FQ_dbl);

    FQ_dbl=((double)PD_input)/PD_output;
    printf("\nFQ_dbl: %d, FQ_dbl:%f",FQ_dbl,FQ_dbl);
    printf("\n********************");
    uint16_t target=7341;
    double target_dbl=target/1000;
    printf("\ntarget_dbl: %d,target_dbl:%f",target_dbl,target_dbl);
    return FQ_u16;
}

I get this as output:

  • "FQ_u16: 3, FQ_u16_raw: 2"
  • "FQ_dbl: 0, FQ_dbl:0.000000"
  • "FQ_dbl: 0, FQ_dbl:0.000000"
  • "FQ_dbl: 942797699, FQ_dbl:0.000000"
  • "********************"
  • "target_dbl: 0,target_dbl:0.000000"
yaviens
  • 25
  • 7
  • 4
    All of your examples are doing integer division, and only then converting to the output type. – stark Nov 20 '21 at 17:30
  • 2
    So you should cast one of the operands to the destination type. `double FQ_dbl = (double)PD_input/PD_output;` but in the last example you convert it back to 16-bit so it's unclear what that is trying to achieve. If you actually want a rounded integer value you could add half the divisor (shift it right) to the numerator before doing the integer division (if there is headroom, and if `int` is 32 bits there will be due to type promotions). So `uint16_t FQ_u16 = (PD_input + (PD_output >> 1)) / PD_output;` – Weather Vane Nov 20 '21 at 17:40
  • What do you mean you "get" zero? How are you outputting the values that makes you think they are zero? You have rounding errors obviously, but none of these should be zero. The last one should be 7.000 for example. – Tom V Nov 20 '21 at 18:16
  • 2
    Re the added code: you are passing `double` to `%d`. You can't do this in a variadic function, and it can affect the second value printed too (which it obviously did). – Weather Vane Nov 20 '21 at 18:46
  • Rule of thumb: _never_ mix fixed point and floating point calculations in the same expression. If you need the division to be carried out in fixed point (which doesn't seem to be the case), do it as a separate operation. If you need it to be carried out in floating point, then make sure that all operands in the expression are floating point types. It really is that simple. With `0` being a fixed point `int` constant, `0.0` being a floating point `double` constant and `1.0f` being a floating point `float` constant. – Lundin Nov 23 '21 at 12:44

1 Answers1

0

For a divide operation to be promoted to double at least one of teh operatnds must be double. To that end you need to cast one or both operands, not the expression after the divide operation. So in:

double FQ_dbl = (double)(PD_input/PD_output);  

PD_input/PD_output is an integer divide which you then explicitly cast to double (though it would implicitly converted in any case).

What you need in this case is:

double FQ_dbl = (double)PD_input / PD_output ;  

Casting PD_input to double implicitly promotes PD_output to double and provided a floating point divide.

You could equally be explicit throughout:

double FQ_dbl = (double)PD_input / (double)PD_output ;  

but it is equivalent.

For expressions involving literal constants such as:

double target_dbl = target / 1000 ;

it is sufficient to use a double literal as follows:

double target_dbl = target / 1000.0 ;

but again:

double target_dbl = (double)target / 1000.0 ;

is the explicit equivalent.

As an aside, you are consistently using the wrong formatter (%d) for outputting a double. You need %f. Include -Wformat (GCC) in your compile options to catch such errors (I think -Wformat is implicit in Wall that you should use in any case together with -Werror - do not ignore the warnings, they are often indicative of semantic errors as opposed to syntactic errors).

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • Nitpick: there are implicit and explicit _conversions_. A cast is an explicit conversion using the `()` cast operator. There are no implicit casts. – Lundin Nov 23 '21 at 12:39
  • @Lundin Accepted and changed, though I think "implicit cast" perhaps better suggests semantic equivalence. But I have stated the equivalence... _explicitly_ ;-) – Clifford Nov 23 '21 at 18:39