String to number is completely wrong

I'm trying to process a string to get the numerical values from the string.
The functions toInt(), toFloat() or toDouble() are giving completely wrong values for the conversion.

String  temp;
int tmp[20];

void setup() {
  Serial.begin(115200);
  // put your setup code here, to run once:
  String value = "$20$(-3.7001624954631467 139.85888350863482),(48.1492194613472 18.3989561222478),(-46.42921396602168 -45.14057884500802),(60.61327990751049 -117.32494850168642)@4";
  Serial.println(value); 

  tmp[0] = value.indexOf("$");
  tmp[1] = value.indexOf("$", tmp[0]+1 );
  temp = value.substring(tmp[0]+1 , tmp[1]);
  uint8_t command_code = temp.toInt();

  tmp[2] = value.indexOf("@");
  temp = value.substring(tmp[2]+1, tmp[2]+2);
  uint8_t length = temp.toInt();

  
  Serial.print("Command code:");  Serial.print(command_code);
  Serial.print("\tlength:");  Serial.println(length);
  
  temp = value.substring(tmp[1]+1, tmp[2]);
  Serial.print("\ttemp:");  Serial.println(temp);

  for(byte coodi = 0; coodi < length; coodi++){
    
    tmp[3] = temp.indexOf(",");
    String cood = temp.substring(0, tmp[3]); 

    Serial.print("\tcood:");  Serial.print(cood);

    tmp[4] = cood.indexOf("(");
    tmp[5] = cood.indexOf(".");
    tmp[6] = cood.indexOf(" ");
    tmp[7] = cood.indexOf(".", tmp[6]);
    tmp[8] = cood.indexOf(")");
    Serial.print("\tlat:");    Serial.print(cood.substring(tmp[4]+1, tmp[5]));
    Serial.print(".");    Serial.print(cood.substring(tmp[5]+1, tmp[6]).toInt());
    // Serial.print("\tlat:");    Serial.print(cood.substring(tmp[4]+1, tmp[6]).toFloat(), 16);
    Serial.print("\tlon:");    Serial.print(cood.substring(tmp[6]+1, tmp[7])); 
    Serial.print(".");    Serial.print(cood.substring(tmp[7]+1, tmp[8]).toInt()); 
    // Serial.print("\tlon:");    Serial.print(cood.substring(tmp[6]+1, tmp[8]).toFloat(), 16); Serial.println();
    temp = temp.substring(tmp[3]+1);
  }
}

void loop() {
  // put your main code here, to run repeatedly:

}

The output is:
$20$(-3.7001624954631467 139.85888350863482),(48.1492194613472 18.3989561222478),(-46.42921396602168 -45.14057884500802),(60.61327990751049 -117.32494850168642)@4

Command code:20 length:4

temp:(-3.7001624954631467 139.85888350863482),(48.1492194613472 18.3989561222478),(-46.42921396602168 -45.14057884500802),(60.61327990751049 -117.32494850168642)

cood:(-3.7001624954631467 139.85888350863482) lat:-3.-666536661 lon:139.1889845370

cood:(48.1492194613472 18.3989561222478) lat:48.1840961760 lon:18.-463395506

cood:(-46.42921396602168 -45.14057884500802) lat:-46.1788413240 lon:-45.456540994

cood:(60.61327990751049 -117.32494850168642) lat:60.152731465 lon:-117.-872392894

You can see the decimal places are wrong with toFloat() or with toInt().

Please suggest a solution or a different code.

I don't see where you used toFloat() and toDouble() methods in your code.
As about toInt() method, it looks to operate correct.

Using a toInt() to extract an intetger and fractional parts of float separately seems to be a wrong approach to me.

Sorry the original code is commented, cause I tried 2 methods to separate the integer and fraction.

Serial.print(cood.substring(tmp[4]+1, tmp[6]).toFloat(), 16);

Even if I separate the factional part and process it separately as an integer with toInt().
My assumption was, since the numbers are very long the conversion doesnt work,

The toInt(), toFloat() or toDouble() only work for less than 3 decimal places.

If you have a solution for longer decimal places please let me know.

Please show the output of the code using a toFloat method

String  temp;
int tmp[20];

void setup() {
  Serial.begin(115200);
  // put your setup code here, to run once:
  String value = "$20$(-3.7001624954631467 139.85888350863482),(48.1492194613472 18.3989561222478),(-46.42921396602168 -45.14057884500802),(60.61327990751049 -117.32494850168642)@4";
  Serial.println(value); 

  tmp[0] = value.indexOf("$");
  tmp[1] = value.indexOf("$", tmp[0]+1 );
  temp = value.substring(tmp[0]+1 , tmp[1]);
  uint8_t command_code = temp.toInt();

  tmp[2] = value.indexOf("@");
  temp = value.substring(tmp[2]+1, tmp[2]+2);
  uint8_t length = temp.toInt();

  
  Serial.print("Command code:");  Serial.print(command_code);
  Serial.print("\tlength:");  Serial.println(length);
  
  temp = value.substring(tmp[1]+1, tmp[2]);
  Serial.print("\ttemp:");  Serial.println(temp);

  for(byte coodi = 0; coodi < length; coodi++){
    
    tmp[3] = temp.indexOf(",");
    String cood = temp.substring(0, tmp[3]); 

    Serial.print("\tcood:");  Serial.print(cood);

    tmp[4] = cood.indexOf("(");
    tmp[5] = cood.indexOf(" ");
    tmp[6] = cood.indexOf(")");

    Serial.print("\tlat:");    Serial.print(cood.substring(tmp[4]+1, tmp[5]).toFloat(), 16);
    Serial.print("\tlon:");    Serial.print(cood.substring(tmp[5]+1, tmp[6]).toFloat(), 16); Serial.println();
    temp = temp.substring(tmp[3]+1);
  }

}

void loop() {
  // put your main code here, to run repeatedly:

}

Output
$20$(-3.7001624954631467 139.85888350863482),(48.1492194613472 18.3989561222478),(-46.42921396602168 -45.14057884500802),(60.61327990751049 -117.32494850168642)@4
Command code:20 length:4
temp:(-3.7001624954631467 139.85888350863482),(48.1492194613472 18.3989561222478),(-46.42921396602168 -45.14057884500802),(60.61327990751049 -117.32494850168642)
cood:(-3.7001624954631467 139.85888350863482) lat:-3.7001626491546630 lon:139.8588714599609375
cood:(48.1492194613472 18.3989561222478) lat:48.1492195129394531 lon:18.3989562988281250
cood:(-46.42921396602168 -45.14057884500802) lat:-46.4292106628417968 lon:-45.1405754089355468
cood:(60.61327990751049 -117.32494850168642) lat:60.6132774353027343 lon:-117.3249511718750000

If you see the output cood string and the lat and lon fractional values you can see they are not similar.

It has nothing to do with bugs in toFloat method - it is limitation a float type itself , it has a maximum precision of 5-6 fraction digits.
And I don't see any errors in output of toInt method

Let's line these up a bit as code to make things clearer

Those numbers are in fact similar. Not the same, but similar. Certainly not "completely wrong".

The issue is that float values have limited precision. You're getting 7 significant digits, which is about right. It is impossible to accurately express 60.61327990751049 with 32-bit floats. Play with the dials on float.exposed to see why.

@tony_neol2024
Why do you need a so many digits? It seems to be a completely pointless.

The circumference of the earth is about 25,000 miles [40,075 km], or 131.5 Mfeet. That divided by 360 is 365 kfeet/degree [111.3 km/degree]. A degree value with 4 decimal places has a implied accuracy of 0.00005 deg, or 18 feet [5.5 m]. That's roughly the accuracy of a typical consumer grade GPS anyway, so there is little point going farther.

surveying - How many decimal places of GPS should be stored to be accurate within a few feet? - Engineering Stack Exchange.

Just trying to get the original number and preserve precision up to 1cm.

I did it on a esp32 with toDouble() and got up to 12 decimal places accurate, though not all.
That works for me. Thanks everyone.

Correct. Standard Arduino doesn't support a double type, it has only float implemented.

If you can moderate the above String to the following format, then you can easily apply the strtok() fuction to extract the numerical data items:

char myValue[] = "-3.7001624954631467,139.85888350863482,48.1492194613472,18.3989561222478,-46.42921396602168,-45.14057884500802,60.61327990751049, -117.32494850168642";
-3.700163
139.858871
48.149219
18.398956
-46.429210
-45.140575
60.613277
-117.324951

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.