When to use casting in C - c

I have a statement in C code which I suspect may be giving me periodic errors, so want to make sure I am doing the right thing as it mixes types.
Objective is to change timebase from 1/32768 seconds to 1/1024, with all times 32 bit integers.
What I have is this:
ts_sys = latest_timestamp * VELO_TICKS_FROM_RTC;
Where ts_sys and latest_timestamp are both unsigned 32 bit integers.
VELO_TICKS_FROM_RTC is a define as follows:
#define VELO_TICKS_PER_SECOND 1024
#define VELO_TICKS_FROM_RTC (VELO_TICKS_PER_SECOND / 32768.0f)
Should I be using a cast here to make sure the division doesn't return an integer (which would be zero) and therefore return the wrong thing? For example would this be better:
ts_sys = (uint32_t) ((float)latest_timestamp * VELO_TICKS_FROM_RTC);
but that seems like overkill..

Should I be using a cast here to make sure the division doesn't return an integer (which would be zero) and therefore return the wrong thing?
no, you are doing A/B, and B is a float, so the compiler promotes A to float and the result is a float!

"Should I be using a cast here to make sure the division doesn't return an integer?"
No, 1024 is of type int and latest_timestamp is of type uint32_t. Both get converted to float in the arithmetic expressions before the respective calculation is done:
Otherwise, if the corresponding real type of either operand is float, the other operand is converted, without change of type domain, to a type whose corresponding real type is float.
C18, §6.3.1.8/1; "Usual arithmetic conversions"
"but that seems like an overkill..."
It is.

Related

Seemingly pointless operations in a macro

The following macro is from an MCAL source of a microcontroller and it converts timer ticks to milliseconds.
#define TICKS2MS(x) ( (uint64) (((((uint64)(x)) * 1) + 0) / 100000) )
Could you please help me understand the significance of multiplying by 1 and adding 0?
The multiplication and addition are in fact pointless, as is the outer cast.
Both operators perform the usual arithmetic conversions on both operands.
For the multiplication, the left operand has type uint64 (as a result of the cast) and the right operand has type int. Since uint64 is the larger type it will be the type of the result. The operand 1 does not change value as a result of the conversion, so in multiplying by 1 the result has the same type and value as (uint64)(x).
Similarly for the addition, the operands are of type uint64 and int respectively, meaning the resulting type is uint64, and 0 does not change value after the conversion. So by adding 0 the result has the same type and value as (uint64)(x) * 1 which has the same type and value as (uint64)(x).
The cast at the end is also superfluous, as the casted expression already has type uint64. As above, the division operator performs the usual arithmetic conversions on its operands so dividing a uint64 by an int results in a uint64.
So the above macro is equivalent to:
#define TICKS2MS(x) ((uint64)(x) / 100000)
I see the words MCAL, TICKS2MS(x) and uint64 (not the uint64_t from stdint.h) .. and it sounds to me like this is an AUTOSAR Environment. (You should have added the autosartag to the question)
The different explicit casts are then most likely due to MISRA-C checks. The MISRA-C check tools can be quite nitpicking about this.
If that macro TICK2MS(x) is part of an xxx_Cfg.h or xxx_PBCfg.h (where xxx is like Gpt, Mcu or Os), then it is most likely generated by the AUTOSAR/MCAL Driver configuration tool. And then, the factor, offset and divider could be part of adjusting to prescaler and maybe a TimerMaxValue (wrap around) or a correction due to odd prescaler/frequency to ticks constellation, in order to correct the failure. If the config is such that such feature is not used, the default values might be generated by the configuration tool.
Since the macro contains only constants, the compiler can optimize this into a simple ((uint64)(x) / 100000).

C adding int and float, and the type changed

I'm not sure my understanding is right here
int i = 5;
float f = 3.9;
int result = i + f;
then when int and float are being added, 8.9 becomes 8?
does this mean when adding smaller data type to bigger datatype gives the answer in smaller data type?
or is this pringting int because it's type casted because result is declared in int? if so how's this different from putting (int)in front of i + f?
In C, when two real1 arithmetic operands are added, they are converted to a common type:
If either operand is long double, the other is converted to long double, and the result is long double.
Otherwise, if either operand is double, the other is converted to double, and the result is double.
Otherwise, if either operand is float, the other is converted to float, and the result is float.
Otherwise, both operands are integers, and additional rules for integer operands apply.
So, in your i + f, i is converted to a float, the values are added, and the result is a float.
You then assign it to an int. In assignment, the value is converted to the type of the destination. So the float sum is converted to int.
The rules for converting arithmetic operands are called “the usual arithmetic conversions” and are specified in C 2018 6.3.1.8. The rules for assignment are specified in C 6.5.16.1.
Note
1 Complex numbers are handled similarly.
In your specific case the result is 8 because the result of the addition is put into an int.
You can imagine the data types as boxes, each data type has a box size associated with it, while adding a float and an int their corresponding box sizes do not match, an int can be placed inside the box of a float. This is why logically the result of the addition is 8.9 . Unfortunately you want to put this result into a smaller box and this leads to the casting that you mention. Basically bigger boxes have a problem when put into smaller ones, you have to get rid of something.
Answering your questions in order:
Q1:when int and float are being added, 8.9 becomes 8?
A:depends in what you will store it, if you define your result as float you will get the 8.9 result otherwise in your case the part after the deciaml point is thrown away because it can't be stored inside an int "box".
Q2: does this mean when adding smaller data type to bigger datatype gives the answer in smaller data type?
A: not really, again depends in what you will store your result.
Q3:is this pringting int because it's type casted because result is declared in int?
A: yes this assumption is right.
Q4: if so how's this different from putting (int)in front of i + f?
A: while their result are the same for this specific scenario, the difference makes more sense in other scenarios. Imagine having instead of int result a float result, then the value in result would be 8.9 as expected, but if you are to use this casting (int)(i+f)
you would put the result of the addition into a smaller box thus losing data, later on you would put it back into a bigger box but you had already lost some bits and the value in result would be 8.0.
Generally you are safe to cast numeric data types from smaller to bigger ones, this way you won't lose any bits along the way. The other way around if you cast float to int or int to char and so on, you may obtain bad results.

Why does (int) float == float.truncate instead of garbage (How does casting actually work?)

Going on understanding of these datatypes as primitives
(int) char, and (char) int are intepretations of data. (int) c gives the integer value of that character, and (char) 14 gives you back the character encoded by 14.
I've always understood this as being a "memory parse", such that it just takes the value at that position and then applies a type filter to it.
Given that floating points are stored as some version of scientific notation, what is stored in memory should be garbage as an integer. Looking into this utility http://www.h-schmidt.net/FloatConverter/IEEE754.html it appears that the whole number portion is separated.
However, since this is in the higher portion of memory, how does the int cast know to "reformat"? Does the compiler identify that it was a float and apply special handling, or what's going on?
Your understanding of casts is completely wrong. Casts are nothing but explicit requests for a value conversion from one type to another. They do not reinterpret the representation of one type as if it had a different type. The source code:
float f = 42.5;
int x;
x = (int)f;
simply instructs the compiler to produce code that truncates the floating point value of the expression f to an integer and store the result in the object x.
I've always understood this as being a "memory parse", such that it just takes the value at that position and then applies a type filter to it.
That is an incorrect understanding.
The language specifies conversions between the fundamental arithmetic types. Lookup "Usual Arithmetic Conversions" on the web. You will find a lot of links that describe that. For converting a floating point type to an integral type, this is what the C99 Standard has to say:
6.3.1.4 Real floating and integer
1 When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined.
float f = 4.5;
int i = (int); // i is 4
f = -6.3;
i = (int)f; // i is -6

Data types in C

A long double is known to use 80 bits.
2^80 = 1208925819614629174706176;
Why, when declaring a variable such as:
long double a = 1208925819614629174706175; // 2^80 - 1
I get a warning saying: Integer constant is too large for its type.
1208925819614629174706175 is an integer literal, not a double. Your program would happily convert it, but it would have to be a valid integer first. Instead, use a long double literal: 1208925819614629174706175.0L.
Firstly, it is not known how many bits a long double type is using. It depends on the implementation.
Secondly, just because some floating-point type uses some specific number of bits it does not mean that this type can precisely represent an integer value using all these bits (if that's what you want). Floating-point types are called floating-point types because they represent non-integer values, which normally implies a non-trivial internal representation. Due to specifics of that representation, only a portion of these bits can be used for the actual digits of the number. This means that your 2^80 - 1 number will get truncated/rounded in one way or another. So, regardless of how you do it, don't be surprised if the compiler warns you about the data loss.
Thirdly, as other answers have already noted, the constant you are using in the text of your program is an integral constant. The limitations imposed on that constant have nothing to do with floating-point types at all. Use a floating-point constant instead of an integral one.
The value 1208925819614629174706175 is first crated as a const int, and then converted to a long double, when the assignment happens.

Can I compare and add a floating-point number to an integer in C?

Can I compare a floating-point number to an integer?
Will the float compare to integers in code?
float f; // f has a saved predetermined floating-point value to it
if (f >=100){__asm__reset...etc}
Also, could I...
float f;
int x = 100;
x+=f;
I have to use the floating point value f received from an attitude reference system to adjust a position value x that controls a PWM signal to correct for attitude.
The first one will work fine. 100 will be converted to a float, and IEE754 can represent all integers exactly as floats, up to about 223.
The second one will also work but will be converted into an integer first, so you'll lose precision (that's unavoidable if you're turning floats into integers).
Since you've identified yourself as unfamiliar with the subtleties of floating point numbers, I'll refer you to this fine paper by David Goldberg: What Every Computer Scientist Should Know About Floating-Point Arithmetic (reprint at Sun).
After you've been scared by that, the reality is that most of the time floating point is a huge boon to getting calculations done. And modern compilers and languages (including C) handle conversions sensibly so that you don't have to worry about them. Unless you do.
The points raised about precision are certainly valid. An IEEE float effectively has only 24 bits of precision, which is less than a 32-bit integer. Use of double for intermediate calculations will push all rounding and precision loss out to the conversion back to float or int.
Mixed-mode arithmetic (arithmetic between operands of different types and/or sizes) is legal but fragile. The C standard defines rules for type promotion in order to convert the operands to a common representation. Automatic type promotion allows the compiler to do something sensible for mixed-mode operations, but "sensible" does not necessarily mean "correct."
To really know whether or not the behavior is correct you must first understand the rules for promotion and then understand the representation of the data types. In very general terms:
shorter types are converted to longer types (float to double, short to int, etc.)
integer types are converted to floating-point types
signed/unsigned conversions favor avoiding data loss (whether signed is converted to
unsigned or vice-versa depends on the size of the respective types)
Whether code like x > y (where x and y have different types) is right or wrong depends on the values that x and y can take. In my experience it's common practice to prohibit (via the coding standard) implicit type conversions. The programmer must consider the context and explicitly perform any type conversions necessary.
Can you compare a float and an integer, sure. But the problem you will run into is precision. On most C/C++ implementations, float and int have the same size (4 bytes) and wildly different precision levels. Neither type can hold all values of the other type. Since one type cannot be converted to the other type without loss of precision and the types cannot be native compared, doing a comparison without considering another type will result in precision loss in some scenarios.
What you can do to avoid precision loss is to convert both types to a type which has enough precision to represent all values of float and int. On most systems, double will do just that. So the following usually does a non-lossy comparison
float f = getSomeFloat();
int i = getSomeInt();
if ( (double)i == (double)f ) {
...
}
LHS defines the precision,
So if your LHS is int and RHS is float, then this results in loss of precision.
Also take a look at FP related CFAQ
Yes, you can compare them, you can do math on them without terribly much regard for which is which, in most cases. But only most. The big bugaboo is that you can check for f<i etc. but should not check for f==i. An integer and a float that 'should' be identical in value are not necessarily identical.
Yeah, it'll work fine. Specifically, the int will be converted to float for the purposes of the conversion. In the second one you'll need to cast to int but it should be fine otherwise.
Yes, and sometimes it'll do exactly what you expect.
As the others have pointed out, comparing, eg, 1.0 == 1 will work out, because the integer 1 is type cast to double (not float) before the comparison.
However, other comparisons may not.
About that, the notation 1.0 is of type double so the comparison is made in double by type promotion rules like said before. 1.f or 1.0f is of type float and the comparison would have been made in float. And it would have worked as well since we said that 2^23 first integers are representible in a float.

Resources