serial vs i2c. why?

why can serial print a float or double directly, and I can even specify how many decimal points, but i2c wire can't.
When I write code to individually send each character over i2c, it often rounds the digits off incorrectly.
I only want 1 decimal place.
It seems serial.print does it accurately each time, but wire randomly rounds that 1 decimal place wrongly.

i2C is sending bytes, not text.

If your i2c transmission is actually text, which is unusual, there would be nothing stopping you from sending floats expressed as text with any number of fractional digits that you want.

aren't they both sending bytes?
I like how serial.print can accept a double.
I'm sending data from one arduino to another.
how can I send a double as text, and how could the other arduino receive it?

moggle_spears:
When I write code to individually send each character over i2c, it often rounds the digits off incorrectly. It seems serial.print does it accurately each time, but wire randomly rounds that 1 decimal place wrongly.

The problem has nothing to do with serial or I2C communications. They are both capable of reliably transferring arbitrary data between two devices.

There is a bug in your code. If you want help post your code. Use
</mark> <mark>[code]</mark> <mark>

</mark> <mark>[/code]</mark> <mark>
tags.

Arduinos don't use doubles.

You may have a problem with the defective implementation of sprintf( ) on Arduinos. That problem is not specifically related to either serial or I2c, but it may be contributing to your issue.

Post the code you are using, which is not giving the outcome that you want.

thanks, help would be appreciated. Code coming soon. Will edit it down to just the i2c bits when I get a chance and post.

But remember, floats are 99% of the time not the best choice for storing your data. I have actual never seen a sketch on this forum that justifies the use of a float... They are slow and take a lot of memory... And yeay, a lot of tutorials tell you to use a float but it's most of the time smarter not to.

Floats are only good if:

  • You have a very (, very) large number (like 100 billion)
  • Or you have two values you want to add but they are in a complete different range (adding 0.02 to 1000).

I'm using some sensors and a library that came with it. When I request the data from the sensor circuit, it returns a double. I really only want the data to 1 decimal place, and I guess 0.1 drift isn't a deal breaker, but it just annoys me how simple data transfer loses accuracy so easily...

Take this code for example. This isn't even using i2c. It's just using the methods I'm using to transmit doubles and receive them over i2c in my main code. The 5.60 and 8.90 are received as 5.5 and 8.8.
(Actually, upon further testing, I think this only happens to doubles that start with only 1 significant figure. I think...?)

void setup() {
Serial.begin(9600);
  double temp1 = 2.345678;
  double temp2 = 5.60;
  double temp3 = 8.90;
  double temp4 = 1.2;
  double temp5 = 3.9;
  double temp6 = 6.0;
  
Serial.println("orginal doubles:"); 
Serial.println(temp1);
Serial.println(temp2);
Serial.println(temp3);
Serial.println(temp4);
Serial.println(temp5);
Serial.println(temp6);
  
  byte data[12] = {0,0,0,0,0,0,0,0,0,0,0,0};

data[0] = temp1;
data[1] = (temp1 - int(temp1)) * 10;
data[2] = temp2;
data[3] = (temp2 - int(temp2)) * 10;
data[4] = temp3;
data[5] = (temp3 - int(temp3)) * 10;
data[6] = temp4;
data[7] = (temp4 - int(temp4)) * 10; 
data[8] = temp5;
data[9] = (temp5 - int(temp5)) * 10;
data[10] = temp6;
data[11] = (temp6 - int(temp6)) * 10; 

Serial.println("array contents:");
for (int i = 0; i < 12; i++) {
 Serial.print(data[i]); 
 Serial.print(" ");
}
Serial.println();

       double temp7 = double(data[0]);
        temp7 = temp7 + (double(data[1]) / 10.0);
        double temp8 = double(data[2]);
        temp8 = temp8 + (double(data[3]) / 10.0);
        double temp9 = double(data[4]);
        temp9 = temp9 + (double(data[5]) / 10.0);
        double temp10 = double(data[6]);
        temp10 = temp10 + (double(data[7]) / 10.0);
        double temp11 = double(data[8]);
        temp11 = temp11 + (double(data[9]) / 10.0);
        double temp12 = double(data[10]);
        temp12 = temp12 + (double(data[11]) / 10.0);

Serial.println("received doubles:");
Serial.println(temp7);
Serial.println(temp8);
Serial.println(temp9);
Serial.println(temp10);
Serial.println(temp11);
Serial.println(temp12);

}

void loop() {


}

To sent and receive a float:

void sentFloat(float val){
  byte * b = (byte *) &val;
  for(byte i = 0; i < sizeof(input); i++){
    Wire.write(b[i]);
  }
}

float receiveFloat(){
  float output;
  byte * b = (byte ** &output);
  
  while(Wire.available() < sizeof(output)){
  }
  for(byte i = 0; i < sizeof(input); i++){
    b[i] = Wire.read();
  }
  return output;
}

septillion:
But remember, floats are 99% of the time not the best choice for storing your data. I have actual never seen a sketch on this forum that justifies the use of a float... They are slow and take a lot of memory... And yeay, a lot of tutorials tell you to use a float but it's most of the time smarter not to.

Floats are only good if:

  • You have a very (, very) large number (like 100 billion)
  • Or you have two values you want to add but they are in a complete different range (adding 0.02 to 1000).

This is pretty poor advice. I agree, there is not much point using a float for some calculations, but for others, they are almost indispensable.

If your claim that you have never actually seen a sketch on this forum that "justifies" the use of a float, then all I can conclude from that is that you have actually seen hardly any sketches.

Any sketch that uses an accelerometer to estimate orientation, even in one direction, is impractically difficult to implement without using floats. And there have been hundreds of them. Do you want to implement an integer version of the arctan function. Go right ahead. Any trigonometric calculation is difficult if you want to avoid floating point types.

Any calculation requiring the evaluation of equations, it is likely to be harder to implement the workaround using integers, than just using the float.

If your algorithm requires you to add 2 numbers like 1000+0.02, you will have scaling issues whether you use floating point or integer types, and you should probably review your process.

michinyon:
If your claim that you have never actually seen a sketch on this forum that "justifies" the use of a float, then all I can conclude from that is that you have actually seen hardly any sketches.

Then you make the wrong conclusion :wink: But I'm not talking about all the well written libraries but sketches posted here with some kind of error. Using a float for storing something like a temperature, distance etc is a wast to use a float. Instead of storing a distance in meters, store it in millimeters of instead of Celsius use deci-Celcius etc. So yeay, 99% of the time a float is a wast of processor power.

But why would you like to use arctan? Functions like that are great on a computer but on a poor 8-bit micro they are heavy.

But why would you like to use arctan? Functions like that are great on a computer but on a poor 8-bit micro they are heavy.

Well how many people have built balancing robots and homemade Segway clones using Arduinos ?

And how many people have built robot arms and need trigonometry or even a simple square root calculation using Arduinos ?

And just an hour ago there was a question about how to invert a 3x3 matrix. Good luck trying to do matrix calculations using integer arithmetic.

You can't solve all of those issues by using millimetres instead of metres.

Some floating point calculations take longer than integer ones. But if you actually implement trigonometry functions using integer arithmetic, you will find it isn't much faster. And the poor little 8 bit micro ? Will it get overloaded ? Will it get worn out ? No, it won't. And it's not like it has anything else useful to do.

michinyon:
Well how many people have built balancing robots and homemade Segway clones using Arduinos ?

I have to give you that one. Never using an Arduino. Used a ARM board for that one.

michinyon:
And how many people have built robot arms and need trigonometry or even a simple square root calculation using Arduinos ?

I did

But yeay, in those situations it is proper use of the float. I never said you should never use a float. But 99% of the sketches on here are not that complex. :wink: And yeay, poor 8-bit. No, it will not overheat or wear out. But in a lot of cases on this forum (and again, not all) it will just piss away time.

Going back to the Original Post.

Floating point variables aren't stored like integer variables in the computer.
In an integer variable there are some number of bits that represent the number, depending on the size (and compiler and hardware) this can be 4, 8, 16, or 32 bits. All of the bits are used to represent the number.
But with a floating point number, only part of the bits are used to represent the number, and part of the bits are used to represent the "exponent". So a number like 5.6 isn't stored as 5.6, but rather as 56 divided by 10.
Unfortunately, the computer doesn't think in base 10, but in base 2, so it's not actually stored as 56 divided by 10 ten, but rather a number that when divided by a power of 2 will equal 56 and the power of 2 required to convert that number to 56.

Since not all numbers convert easily to powers of 2, you can end up with numbers that are "stored" a little bit different than you might think.

This is not true for all numbers, but can be true for some numbers.

Let's take your 5.60 number as an example.
I don't know the exact value stored in the Arduino because I don't have one with me where I am right now, and I don't even know which Arduino you're using, and it COULD be different on different Arduino models.
But, its very likely that this number is stored as 5.5999999999999999999 rather 5.60.

In your program, you take the integer of this number and store it in a byte array.
Then you "chop off" the integer portion, leaving .5999999999999999, which you then multiply by 10 and store in a byte array.
.599999999999 times 10 is 5.999999999999 when stored in a byte array, it must be converted to an integer, so the .9999999999 is cut off, and the result is 5.

When you "decode" this later, you get 5.5 instead of 5.6 because you lost the .99999999999

If you want to continue to do it this way (but you should use the alternate method described in another post) then you would need to "round" the data rather than multiply by 10 and truncate it.
So you could multiply by 10 and then add .5, then store it in the byte array and then you would get the result you're looking for.

Awesome thanks so much for your replies. Such good info there. I originally was using serial to "dump" the data to any serial device listening and it was working well. Then I heard about i2c and liked the idea of "requesting" the info from a device only when required.
I don't have a lot of programming experience and I don't know how to use pointers. Perhaps that should be my next thing to try and master.
I'll do some more tinkering and report back with more details of my project... thanks again...