I've got a few questions with regard to data types. In the Blink Without Delay Sketch I noticed that the following line of code compares an unsigned long to a long.
if (currentMillis - previousMillis >= interval) {
Is this an oversight or acceptable in some or all cases?
Secondly, I noticed that functions expect a data type. The function Serial.begin() for instance expects a long to set the speed of serial communication.
Parameters
speed: in bits per second (baud) - long
Given that the rates 300, 600, 1200, 2400, 4800, 9600 all fall within the size of a int is the following acceptable?
const int baud = 9600;
Alternatively I noticed functions output a data type. The function atof(const char* str) parses a C string and outputs it as a floating point number. I assume the following is correct?
float pi = atof(&string);
This leads me to my final question. Let's say I'm only interested in the integer portion of the above code. Is the following acceptable?
I think you should use a cast anytime the expression types in an expression don't match. The Gnu compiler that hides beneath the IDE is pretty good at figuring such things out. Most compilers perform silent casts without complaint. A silent cast occurs whenever the expression types don't match, but destination expression is "larger" than the source expression. For example, the assignment statement has the source expression on the right requiring less memory for storage than the destination expression on the left:
int a;
long b;
a = 10;
b = a; // Silent cast
The compiler doesn't normally complain about this; a 2-byte bucket can have its contents poured into a 4-byte bucket without slopping data on the floor. However:
int a;
long b;
b = 10000L;
a = b; // uh-oh
might be a problem because you are trying to pour 4 bytes of data into a 2-byte bucket, which runs the risk of spilling 2-bytes of data on the floor. Most compilers can still figure this out, but an explicit cast:
int a;
long b;
b = 10000L;
a = (int) b; // explicit cast
is a good idea if for no other reason than it documents your intention.
First and foremost I want to apologize for taking so long to get back to you. So what happens in the following?
unsigned long a = 1000000;
unsigned long b = 1020000;
int c;
c = b - a;
In the above example the answer is 20000 which is well within the capacity of an integer. My concern is can I cast variables a and b to integers given they are too big for an integer?
unsigned long a = 1000000;
unsigned long b = 1020000;
int c;
c = (int)b - a;//OH NO WHAT HAPPENS HERE?
And now your topic is "the evaluation of expressions in C/C++". I can't see why you (or any one else) should expect people to do a write up for when there are already 1,000,000 of words writen on the subject.
You need to learn to stick your nose in the text books, and stop expecting other people to do the work for you!
Is this an oversight or acceptable in some or all cases?
oversight (all of them should probably be declared unsigned long), but also acceptable in most cases.
C will do a bunch of "implicit casting", for you. Frequently that does the correct thing. Sometimes it'll bite you. Note that constants are ints until something makes them obviously otherwise, so theoretically a statement like
unsigned int count = 5000;
Should really be one of
unsigned int count = (unsigned int)5000;
unsigned int count = 5000U;
But you're really getting into the realm of syntax that everyone would find annoying.
Secondly, I noticed that functions expect a data type. The function Serial.begin() for instance expects a long to set the speed of serial communication.
is the following acceptable?
Code: [Select]
const int baud = 9600;
I guess. There's not much reason to do so; constants will probably not end up taking any space in the final image, anyway. 115200, another common speed, does NOT fit in an int.
Is the following acceptable?
Code: [Select]
int pi = atof(&string);
Or should I explicitly cast?
Code: [Select]
int pi = (int)atof(&string);
I would explicitly case here, to show that it's really what you want to do. Note that the implicit casting rule for float to int does not round, making this one of the cases where you are likely to be bitten. It's not unusual for float values to be inexact: 1.9999982 instead of 2.0, for instance, and if you assign 1.9999982 to an int, you'll get 1.
@homes4
I don't agree with your assertion that I'm simply wanting members to solve my problem. For the record I'm a sucker for a good manual. In fact I love reading them. I only post in the forum if I'm struggling with a concept. I know from experience that manuals don't always give you the full picture and sometimes you need someone to the discuss the issue.
@westfp
It appears to me that if implicitly casting one data type to another gives unexpected results I need to explicitly cast.
@Nick Gammon
I agree the topic is not hardware related. It is however software related. The Arduino Reference does explain data types however IMHO a trap for young players (I being one of them) is mixing data types. Data types are the building blocks of your program. The way you put them together is where the magic happens.
The Arduino Reference is just that. It does not pretend to be a C or C++ programming guide, nor should it, but there are plenty of them around on the Web.
It appears to me that if implicitly casting one data type to another gives unexpected results I need to explicitly cast.
An explicit cast gives you the same results, with a bit of added "I meant to do that."
int pi = (int)atof(&string);
This, by the way, has to be a horribly inefficient way to convert the "numeric string up to the decimal point" to an integer. The "atoi" function should end up doing the same thing.
3794 byte code vs 2174 bytes of code for:
char *mystring;
// #define USEFLOAT 1
void setup() {
Serial.begin(115200);
// put your setup code here, to run once:
if (digitalRead(1) == HIGH) { // Try to make sure things aren't optimized away...
mystring = "1234.5678";
} else {
mystring = "5678.1234";
}
}
void loop() {
// put your main code here, to run repeatedly:
#ifdef USEFLOAT
int i = (int)atof(mystring);
#else
int i = atoi(mystring);
#endif
Serial.println(i);
delay(10000);
}
The proper term is implicit cast or implicit conversion, not silent cast like econjack states. It's best to use the conventional terminology if ilovetoflyfpv is going to be googleing around.
WARNING Advanced content WARNING
In addition to conversions between the primitive types, conversions between classes is also possible if the destination class has a constructor that takes the source class as its only argument and that constructor is not declared explicit, or if there is an explicit conversion function for the destination type defined in the source class.
To make this a little less abstract, the Arduino's EEPROM library makes use of this.
The begin() function, in order to make use of iterator coding patterns, returns a custom class called EEPtr, which is coded to act just like a pointer to an EEPROM cell.
EEPtr begin() { return 0x00; }
The function is declared to return an EEPtr, but the actual return statement has an integer literal in it. How does the compiler know how to do the conversion? By the constructor defined in the EEPtr class:
EEPtr( const int index )
: index( index ) {}
The read() function is like this:
uint8_t read( int idx ) { return EERef( idx ); }
The function is defined to return a uint8_t (unsigned 8-bit integer), but the return statement is throwing up an EERef object. How is the conversion from a custom class to a primitive type handled? By the conversion function defined in EERef: