Кодирование чисел в BCD (последовательный интерфейс Casio)

Я пытаюсь создать устройство, которое взаимодействует с калькулятором Casio fx-9750 через последовательный порт с Arduino. Я понял, как получать значения и декодировать BCD, но я застрял в том, как создать требуемые значения из числа с плавающей запятой (для передачи обратно).

Калькулятор отправляет пакет данных, который имеет значение экспоненты, несколько значений данных и байт, содержащий информацию об отрицательности, мнимых частях и т. д. Каждое значение данных стоит одну сотую от предыдущего, поэтому первое - это количество 10 с, затем 0,1 с, затем 0,001 с и т. д. Это продолжается до 0,0000000000001 с, хотя это выходит за пределы того диапазона, который мне действительно нужен, поэтому этот уровень точности не действительно важно для меня. Результат моей принимающей программы выглядит так:

Exponent: 1
10s: 1
0.1s: 23
0.001s: 40

Это соответствует 12.34.
Общее уравнение, которое я разработал, было: (пусть a = 10 с, b = 0,1 с, e = показатель степени и т. д.)

((a*10)+(b*0.1)+(c*0.001))*10^(E-1)

Если показатель степени изменится на два:

Exponent: 2
10s: 1
0.1s: 23
0.001s: 40

Это составит 123,4

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

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

Каков самый быстрый и простой способ перейти от обычного числа (например, 123,4) к такому расположению? Было бы очень признательно за решение на языке Arduino, но любое понимание необходимого математического процесса было бы равноценно.

Редактировать относительно поплавков: Я должен уточнить - я буду иметь дело с числами с плавающей запятой в других частях моей программы и хотел бы, чтобы введенные мной значения были совместимы с числами любого размера (в пределах разумного, как указано ранее). У меня нет проблем с умножением их на целые числа или приведением их к другим типам данных.

Какое отношение имеет BCD к плавающей запятой? Зачем нужна плавающая точка? Если вам нужна плавающая точка, Arduino - ужасный выбор, учитывая, что AVR - это устаревшая 8-битная архитектура без FPU.

Lundin 20.08.2018 11:29
how do I go from a normal number (123.4) into this arrangement. разделите на десять (или сто), пока оно не станет от 1 до 10. (или от 1 до 100). Подсчитайте количество делений.
joop 20.08.2018 11:31
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
137
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ха, это было весело!

#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <float.h>

struct num_s {
  // exponent
  int e;

  // v[0] is *10
  // v[1] is *0.01
  // v[2] is *0.0001
  // and so on...
  // to increase precision increase array count
  int v[6];
};

#define NUM_VALSCNT (sizeof(((struct num_s*)0)->v)/sizeof(((struct num_s*)0)->v[0]))

// creates num_s object from a double
struct num_s num_create(double v) {
  struct num_s t;

  // find exponent so that v <= 10
  t.e = 0;
  while (fabs(v) >= 10.0) {
    ++t.e;
    v /= 10.0;
  }

  // for each output number get the integral part of number
  // then multiply the rest by 100 and continue
  for (size_t i = 0; i < sizeof(t.v) / sizeof(t.v[0]); ++i) {
    const double tmp = fmod(v, 1.0);
    t.v[i] = v - tmp;
    v = tmp * 100;
  }
  return t;
}

// converts back from num object to double
double num_get(struct num_s t) {
  double denom = 10;
  double ret = 0;
  for (size_t i = 0; i < sizeof(t.v) / sizeof(t.v[0]); ++i) {
    ret += denom * t.v[i];
    denom /= 100;
  }
  return ret * pow(10, t.e - 1);
}

void num_println(struct num_s t) {
  printf("%f  = ", num_get(t));
  for (size_t i = 0; i < sizeof(t.v) / sizeof(t.v[0]); ++i) {
    printf(" %d", t.v[i]);
  }
  printf(" %d\n", t.e);
}

// returns the precision of numbers
// the smallest number we can represent in num object
double num_precision(void) {
  return pow(0.1, (NUM_VALSCNT - 1) * 2) * 10;
}

int num_unittests(void) {
  const double tests[][3] = {
    { 123.49, 123.5, 123.51, }
  };
  for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) {
    const double tmp = num_get(num_create(tests[i][1]));
    if (!(tests[i][0] <= tmp && tmp <= tests[i][2])) {
      return i + 1;
    }
  }
  return 0;
}

int main() {
  num_println(num_create(12.3456789));
  num_println(num_create(123.5));
  num_println(num_create(12.35));
  printf("%d\n", num_unittests());
  return 0;
}

Однако выполнение этого кода на Arduino будет совсем неинтересным. Это будет невероятно медленным и убьет значительное количество вспышки. Оказывается, Arduino - это не ПК.

Lundin 20.08.2018 12:57

Вероятно, потому что с плавающей запятой большие для 8-битных. Вы можете попробовать проверить, насколько велики функции, что занимает больше всего места. Вы можете перейти от double к float, изменив fabs(v) >= 10.0, чтобы сначала проверить, отрицательно ли v. Вы можете заменить fmod только на (int)v. Или вы можете попробовать просто перейти к целым числам с некоторой заранее заданной произвольно выбранной точностью. Передача указателей вместо значений может уменьшить использование флэш-памяти.

KamilCuk 20.08.2018 13:13

Нет, основная проблема в том, что Arduino - это не ПК, поэтому у него нет FPU. И мне непонятно, зачем вообще нужна плавающая точка.

Lundin 20.08.2018 13:24

При этом передача структур по значению - плохая практика на всех компьютерах, даже на ПК.

Lundin 20.08.2018 13:24

Спасибо, код отлично работает. Что касается скорости, я провел тест, используя функцию arduino millis (). На обработку числа 1233.3456789 неоднократно уходило 25 мс. С огромным числом (1233123123134,3456789) потребовалось 26 мс. Я согласен, что это кажется несколько медленным, но у меня очень мало опыта в подобных вещах. Это медленно? и как это можно было улучшить?

Matthew B 21.08.2018 10:05

Другие вопросы по теме