void setup() {
Serial.begin(115200);
float x = 265.41;
float y = x;
float z = x;
x = round(x * 10) / 10; // 265.00
y = round(y * 10); y = y / 10; // 265.40
z = round(z * 10.0) / 10.0; // 265.40
Serial.println(x);
Serial.println(y);
Serial.println(z);
}
void loop() {
}
x: Why does the formula for x return an incorrect result?
y: The one for y is basically the same although split into two parts and that one does give the expected result!
z: When I change "10" into "10.0" it also works fine.
Second question: why does it print "265.40" in the serial monitor, and not "265.4" like I expected? I've never seen this happening in any other programming language...
float is an abuse of Arduino, which doesn't have a proper arithmetic unit. The ATmega328P is an integer only processor. Take your float math and move it up in the range of an int or long then simply address the decimal point in a sprint() statement.
Perehama:
Take your float math and move it up in the range of an int or long then simply address the decimal point in a sprint() statement.
I can't: in the real sketch x is the result of measuring an interval, and the rounded value is used for more calculations. But I'll remember this idea, it may come of use in the future.
TomGeorge:
10 is an int constant.
10.0 is a float constant.
I understood that when I found an example where 10.0 was used instead of 10. Good to know, but it still doesn't explain why the line with x gives a different result than the one with y!
TomGeorge:
265.40 because that is the default decimal place I believe.
I tried Serial.print(1.1), it gives 1.10. The same goes for printing to the LCD. That's just wrong...
gfvalvo:
The second argument defaults to 2 when printing a float.
I didn't know that, that's the solution for my 2nd question!
gfvalvo:
(about printing 265.40 instead of 265.4):
Because your expectations are wrong.
(about other programming languages):
Can't comment on that since I don't know what other languages you've used.
I'd expect Serial as well as LCD to print "1.1" in all three cases, like Clipper end VB6 (my most used languages) do. But hey, I guess I'll have to get used to stumble into issues like this one. It's not the first time I got puzzled by C++..
Erik_Baas:
I'd expect Serial as well as LCD to print "1.1" in all three cases, like Clipper end VB6 (my most used languages) do. But hey, I guess I'll have to get used to stumble into issues like this one. It's not the first time I got puzzled by C++..
It's the Arduino libraries that default to printing 2 digits after the decimal point, not anything that's standard in C++. Writing a floating-point value to std::cout/cerr in C++ prints only as many digits as are necessary by default.
christop:
What would you expect Serial.print(1.10) or Serial.print(1.100) to print?
The choice to print 2 digits after the decimal point by default was arbitrary (IMO), but I wouldn't say it's "wrong".
I wouldn't say it's wrong as well. But certainly could lead to confusion or wondering about this particular default behaviour. I don't mind that behaviour ----- as long as there's some way to turn it off - if there is a way to turn it off that is.
Erik_Baas:
x = round(x * 10) / 10; // 265.00
y = round(y * 10); y = y / 10; // 265.40
z = round(z * 10.0) / 10.0; // 265.40
For x ..... the main catch is that 'round' will cast to an integer type ...... a long integer type. So x * 10 becomes 2654.1, which then gets rounded to 2654, and the value is long INTEGER. Then, an integer division is performed (since the '10' is automatically cast to an integer. This integer division leads to truncation. So 'x' ends up being truncated ..... a value of 265.00
But ---- for 'y', the first value of y becomes 2654. The 'round' function will lead to long integer, but since 'y' is cast as float, then the first 'y' will be 2654.0 ...... and then a divide by 10 will occur. But since one of the arguments in this division is not an integer, then the result will be a 'float', leading to 265.4 or 265.40
Southpark:
For x ..... the main catch is that 'round' will cast to an integer type ...... a long integer type. So x * 10 becomes 2654.1, which then gets rounded to 2654, and the value is long INTEGER. Then, an integer division is performed (since the '10' is automatically cast to an integer. This integer division leads to truncation. So 'x' ends up being truncated ..... a value of 265.00
But ---- for 'y', the first value of y becomes 2654. The 'round' function will lead to long integer, but since 'y' is cast as float, then the first 'y' will be 2654.0 ...... and then a divide by 10 will occur. But since one of the arguments in this division is not an integer, then the result will be a 'float', leading to 265.4 or 265.40
It's unfortunate (and a bug) that Arduino even defines a round macro when a function by the same name exists in "math.h". If that macro didn't exist, we wouldn't even see this surprising and wrong behavior.
One thing you can do in your program is #undef round to remove this damage. Or put parentheses around the function name, as in (round)(x * 10).