RE: Data Types

Hi Gang

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?

int pi = atof(&string);

Or should I explicitly cast?

int pi = (int)atof(&string);

Any help would be greatly appreciated.

Cheers

Jase :slight_smile:

Look up cast's

Mark

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.

Hi econjack

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?

Would love to know your thoughts.

Cheers

Jase :slight_smile:

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!

Mark

if (currentMillis - previousMillis >= interval) {

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.

This is nothing to do with Arduinos. The Arduino IDE uses C++ so I suggest you read up on how C++ works.

Hi Gang

Again thanks for the input.

@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.

Anyway I really appreciate the responses.

Cheers

Jase :slight_smile:

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);
}

Hi westfw

I like 'I meant to do that'.

Cheers

Jase :slight_smile:

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:

   operator const uint8_t() const       { return **this; }

EEPROM.h (5.33 KB)