Int and Float and Print Formating

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 6

3

In the C family of languages, if you divide an integer by another integer, the compiler interprets the
division operator (/) as an integer division, i.e. you get the integer part of the quotient. Thus,

(first_int + second_int + third_int) / 3


is an integer.

If either operand is a floating point number, then the other one is implicitly converted to floating
point, and you get a floating point division. Thus

(first_int + second_int + third_int) / 3.0


is a floating point number, and the result you expect.
Share
Improve this answer
Follow
edited Apr 22 '16 at 13:17
answered Apr 15 '16 at 12:30

Edgar Bonet
36.5k33 gold badges2828 silver badges5858 bronze badges
 shouldn't one of your variables in the second code line be a float? All ints divided will still be an
int – dinotom Apr 22 '16 at 11:54
 3.0, as any constant having a decimal point, is a floating point number. As opposed to 3, which is an
int. – Edgar Bonet Apr 22 '16 at 13:20
add a comment
3
In languages such as C or C++ there is a distinction between integer types and floating-point types.
You see, computers work using bits. A bit is either 0 or 1. A bit obviously lack in both range and
precision, so we combine multiple bits in order to improve.

Integral Types
A byte is a group of 8 bits. In C, the type used is either

uint8_t x; // Values from 0 to 255.


int8_t x; // Values from -128 to 127
char x; // Values from the ASCII character set (still -128 to 127)
On Ardiuno Uno, an int is a group of 16 bits:

uint16_t c; // Values from 0 to 2^16-1 = 65535, synonym: unsigned int (Arduino)


int16_t c; // Values from -32768 to 32767, synonym: int (Arduino)
A long is a group of 32 bits:

uint32_t v; // Values from 0 to 2^32-1 = 4,294,967,295 = 4 GiB (unsigned long)


int32_t v; // Values from -2,147,483,648 to 2,147,483,647 (long)
There is also usually support for 64-bit numbers, but we try not to use them in Arduino code. The
reason is that most Arduino MCUs use 8-bit registers, so 64-bit operations such as "add", "sub", etc.
would be very slow.
Note: Different architectures use different numbers of bits for these types. In order to be compatible
with other architectures, use int32_t etc. to be sure.

Literals and Casts


Oftentimes, the compiler can not see what type a literal value is supposed to be. In C, the compiler
picks a default which is usually larger than you would like. For this reason, it is useful to know how
to specify a literal exactly with the type:

((uint8_t)200) // Casting 200 to an unsigned 8-bit integer


((char)65) // Making the ASCII letter 'A'
((int32_t)42) // Force 32 as a signed 32-bit integer (long)
42L // The 'L' suffix does the same as above

Division and Remainder


A common cause of problems when programming in C is the division operator /. If the operands are
both integer types, the result will also be an integer. So 1/3 is in fact 0. You can get the remainder of
the division using the modulus operation %. For instance:

void show_division(int n, int d)


{
Serial.print("The division: ");
Serial.print(n);
Serial.print(" / ");
Serial.print(d);
Serial.print(" equals ");
Serial.print(n / d);
Serial.print(" with a remainder ");
Serial.println(n % d);
}

Floating-point Types
All of the above types are integrals, so they do not support any fractional value. Therefore C
defines two different floating-point types: float and double. The former is a 32-bit value and the
latter is a 64-bit value. The way it works is that the bits are split in 3:

 A single bit is used for the sign.


 A certain number of bits represent the fractional part, the mantissa.
 The rest of the bits are used for the exponent.
As an example, the 32-bit float value representation for 1.25 is

seeeeeee_emmmmmmm_mmmmmmmm_mmmmmmmm = s * 2^(e-127) * (1.m...)_2


00111111_10100000_00000000_00000000 = (+1) * 2^(127-127) * (1.0100)_2
// Note: Binary 1.01 equals 1.25 in decimal
For performance, use the f suffix
Although 64-bit doubles are very accurate, the resulting code will run much slower. I, therefore,
recommend using 32-bit floats instead. It is useful to know that you can tack on an f after the
floating-point literal:
(a + b + c) / 3.0f; // Make sure to divide using 32-bit operations.
If a, b and c above are integer types, the additions will also be integer additions. When the compiler
sees an int divided by a float, it will convert the int to a float, and then perform a float division.
This is slow, but certainly faster than a double division.

The compiler is smart, but it doesn't hurt to help it. There is only a very small precision loss in
converting this to a float multiplication:

(a + b + c) * (1.0f / 3.0f);
The compiler will pre-calculate (1.0f / 3.0f), so you get a much faster floating-point
multiplication (in addition to the int to float cast).

Note: Arduino Uno defaults all floating point literals to 32-bit, and double is the same as float.
Therefore, the f suffix is strictly not needed. It is still good practice to add these for compatibility
with other architectures.
Share
Improve this answer
Follow
edited Apr 26 '16 at 20:27
answered Apr 22 '16 at 19:23

Pål-Kristian Engstad
13122 bronze badges
 Your answer would be correct on many CPU architectures. However, the question is tagged “arduino-uno”,
which implies an AVR architecture, and there are three specific details in your answer which
are incorrect in this specific architecture: 1) chars are signed. 2) ints are 16 bits, not 32. 3) doubles are 32
bits, just like floats. Then, obviously, the f suffix has no effect at all

Work-around to displaying floats with variable precision


C programmers know about the sprintf function and may be tempted to use sprintf to
format floating point numbers with the standard control over precision and width. Unfortunately,
as of early 2013, the standard Arduino library does not include a full implementation of
the stdio library, such that the Arduino implementation of sprintf cannot be used to display
floating point numbers.
The following example shows some failed and some successful ways of displaying floating point
values. The key is to use the basic conversion utilities dtostrf and dtostre instead of the
higher level sprintf function.

Download the sketch


// File: LCD_format_numbers.ino
//
// Use dtostrf and dtostre to format floating point numbers
// for display on a 20x4 character LCD panel
//
// Gerald Recktenwald, [email protected]
// 2013-02-11

#include // include the library code:


// Create a LiquidCrystial instance connected to pins 8 through 13
LiquidCrystal lcd(8, 9, 10, 11, 12, 13);

void setup() {
lcd.begin(20, 4); // set up the LCD's number of columns and rows:
}

void loop() {

update_LCD(lcd, 1, 2, 3.14, 6.57e-3);


delay(2000);
lcd.clear();
delay(500);
}

// ---------------------------------------------------------------------
// Display values in different formats. Some work, some don't
// In the last row, show how dtostrf and dtostre work
//
void update_LCD(LiquidCrystal lcd, int v1, int v2, float v3, float v4) {

char display_string[40] = ""; // Buffer for 40 character width


char float_str3[16] = ""; // Buffer for string value of float
char float_str4[16] = "";

// -- Row 0: display numbers in default format with one space between


lcd.setCursor(0,0);
lcd.print(v1);
lcd.print(" "); lcd.print(v2);
lcd.print(" "); lcd.print(v3);
lcd.print(" "); lcd.print(v4);

// -- Row 1: display all numbers as 2 digit integers with sprintf


// and no conversion of floats. This does not work
lcd.setCursor(0,1);
sprintf(display_string,"%2d %2d %2d %2d",v1,v2,v3,v4);
lcd.print( display_string );

// -- Row 2: display integers with two digits, and attempt to use the
// standard sprintf notation to convert floats to strings.
// This DOES NOT WORK because the Arduino stdio library does not
// include a full implementation of sprintf.
lcd.setCursor(0,2);
sprintf(display_string,"%2d %2d %4.2f %7.3e",v1,v2,v3,v4);
lcd.print( display_string );

// -- Row 3: display integers as 2 digit integers and use dtostrf


// and dtostre to convert floats to strings. Note that float_str1
// and float_str2 have been declared 16 characters wide. So this
// warning: no precautions against buffer overflow are taken.
// Default formatting of the scientific string is obtained with
// the 0x00 value for __flags in dtostre
lcd.setCursor(0,3);
dtostrf(v3,4,2,float_str3); // Convert v3 to string in float_str3
dtostre(v4,float_str4,3,0x00); // Convert v4 to string in float_str4
sprintf(display_string,"%2d %2d %s %s",v1,v2,float_str3,float_str4);
lcd.print( display_string );
}

#2 Why does Arduino division result in a zero?


Look at this code example. What do you expect the serial monitor to print?

1 float example = 3 / 5 * 2;
2 Serial.print("example = ");
3 Serial.println(example);
// Prints: example = 0.00
4
You might assume that the serial monitor prints “1.20”. Afterall, that is the answer
you get when you plug it into your calculator. However, the Arduino math comes
back with “0.00”. Why does that happen?

Similar to the example above, all of the constants on the right of the equal sign
are integers. So each intermediate math operations get stored an integer data
type.

A straightforward way to fix this problem is to tell the compiler that one of the
constants is a floating point variable. Just adding “.0” promotes the operation to
floating point math. Later I’ll talk about preference order which makes a
difference on where you add the .0. If you want to be safe, add it to all integer
constants.

1 float example = 3.0 / 5.0 * 2.0;


2 Serial.print("example = ");
3 Serial.println(example);
// Prints: example = 1.20
4
What happens if the variable “example” changes into an integer? The decimal
point does not get stored. Let’s look at two examples.

#3 Arduino math does not Round


Starting off, here is the same math operation from above but with “example”
declared as an integer instead of a float.

1 int example = 3.0 / 5 * 2;


2 Serial.print("example = ");
3 Serial.println(example);
// Prints: example = 1
4
An important thing to realize is that when doing decimal math that ends up in an
integer, the decimal is truncated. The decimal is not rounded. For the second
example I changed the math a little bit.

1 int example = 5.0 / 3;


2 Serial.print("example = ");
3 Serial.println(example);
// Prints: example = 1
4
Even though the result is actually “1.666…”, Arduino math returns “1” not “2.” The
integer does not get rounded up (or down) based on the decimal. The code
truncates or drops, the decimal entirely. (Note that there are rounding functions
are available in Math.h.)

#4 Arduino’s float Precision


Floating point variables mean that the decimal point can float around. The
precision of the number is limited to a certain number of digits. That is not the
same as saying a certain number of decimals. In the case of 8-bit AVR Arduino
boards, the float type is 32-bits which makes the limit 6 or 7 digits.

1
float example1 = 1.23456789;
2 float example2 = 123456.789;
3 Serial.print("example1 = ");
4 Serial.println(example1,9);
5 Serial.print("example2 = ");
6 Serial.println(example2,4);
7  
// Prints:
8 // example1 = 1.234567880
9 // example2 = 123456.7890
10

You might also like