Синтаксический анализ Arduino Double неверен

У меня проблемы с анализом GPS-координат с помощью Arduino ide.

Честно говоря, я хочу обрабатывать данные без библиотеки.

От моего датчика я получаю эту строку: 4815.98450

Результат String.toDouble(): 4815.9843750

Я также разобрал его самостоятельно, но получил тот же результат: (да, этот код нехороший ;))

char buf[10];
  msg.toCharArray(buf, 19);
  double num = 0.0;

  int c;

  c = buf[0] - '0';
  double num0 = (c * 1000);
  c = buf[1] - '0';
  double num1 = (c * 100);
  c = buf[2] - '0';
  double num2 = (c * 10);
  c = buf[3] - '0';
  double num3 = (c * 1);
  c = buf[5] - '0';
  double num5 = (c * 0.1);
  c = buf[6] - '0';
  double num6 = (c * 0.01);
  c = buf[7] - '0';
  double num7 = (c * 0.001);
  c = buf[8] - '0';
  double num8 = (c * 0.0001);
  c = buf[9] - '0';
  double num9 = (c * 0.00001);
  num = num + num0;
  num = num + num1;
  num = num + num2;
  num = num + num3;
  num = num + num5;
  num = num + num6;
  num = num + num7;
  num = num + num8;
  num = num + num9;

Есть ли у кого-нибудь решение моей проблемы?

Большое спасибо :=)

, 👍1


3 ответа


Лучший ответ:

3

Нет простого решения. Используемый вами Arduino не поддерживает с плавающей запятой двойной точности. Тип double — это просто псевдоним для float с 24-битной точностью.

На самом деле ваша программа выдает правильный результат, так как число с плавающей запятой ближайшим к 4815,98450 является именно 4815,984375.

,

Спасибо за ваш ответ! Вы мне очень помогли, потому что теперь я знаю, что это не ошибка. Но теперь я ищу обходной путь :), @Benjamin_Ellmer

@Benjamin_Ellmer: Может быть, хранить целые и дробные части отдельно?, @Edgar Bonet


1

double (или float) — это просто неподходящий тип данных.

Возможно, вы принимаете int(4815) и float(0,98450) как два числа?


Кстати, могу поспорить, что ваш сенсор не получит объект String.

,

Я читаю вывод из TX моего датчика, поэтому я почти уверен, что это строка ;) я понял, что могу выполнить расчет без первых двух цифр числа. есть случаи, когда это не работает, но такие случаи очень маловероятны, @Benjamin_Ellmer


0

Вы можете преобразовать это в длинное, выполнить арифметические действия и преобразовать его обратно в десятичное число ASCII:

Удалить десятичную точку из строки. Преобразуйте оставшуюся строку цифр в длинную. Теперь у вас есть координата в двоичном формате, в единицах младшей значащей десятичной цифры; 1 младший бит == 0,00001, если вы использовали 5 знаков после запятой.

Управляйте своими данными, как вам нужно, используя арифметику длинных целых чисел.

Разделите значение на 10^ - (количество цифр дроби, которые вы использовали). Частное - это целая часть числа; остаток - дробная часть. Выведите частное и десятичную точку. Распечатайте оставшееся заполнение нулями слева по мере необходимости, чтобы в результате напечатать (количество цифр дроби, которое вы использовали) общее количество цифр дроби.

Преобразование вывода становится еще проще, если вы хотите использовать sprintf():
Разделите значение на 10^ - (количество использованных цифр дроби), как указано выше. Предположим, 5 мест, как в вашем примере:

char buffer[BIGENOUGH+1];
sprintf(buffer, "%ld.%05ld", value/100000, value%100000);

Обратите внимание, что я сделал два деления, чтобы получить частное и остаток, и предположил, что вы можете допустить падение производительности. Если вы ожидаете получить много значений, а скорость является фактором, вы можете найти или написать процедуру деления, которая может возвращать как частное, так и остаток от одного деления.

Обновление: @edgar_bonet отмечает, что компилятор достаточно умен, чтобы заметить, что этот фрагмент использует оба результата численного деления и в конце концов избегает двойного деления.

,

Относительно «_подпрограммы, которая может возвращать как частное, так и остаток от одного деления_»: это именно то, что делает avr-gcc при компиляции приведенного вами примера., @Edgar Bonet