Я пытаюсь создать устройство, которое взаимодействует с калькулятором 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, но любое понимание необходимого математического процесса было бы равноценно.
Редактировать относительно поплавков: Я должен уточнить - я буду иметь дело с числами с плавающей запятой в других частях моей программы и хотел бы, чтобы введенные мной значения были совместимы с числами любого размера (в пределах разумного, как указано ранее). У меня нет проблем с умножением их на целые числа или приведением их к другим типам данных.
how do I go from a normal number (123.4) into this arrangement. разделите на десять (или сто), пока оно не станет от 1 до 10. (или от 1 до 100). Подсчитайте количество делений.





Ха, это было весело!
#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 - это не ПК.
Вероятно, потому что с плавающей запятой большие для 8-битных. Вы можете попробовать проверить, насколько велики функции, что занимает больше всего места. Вы можете перейти от double к float, изменив fabs(v) >= 10.0, чтобы сначала проверить, отрицательно ли v. Вы можете заменить fmod только на (int)v. Или вы можете попробовать просто перейти к целым числам с некоторой заранее заданной произвольно выбранной точностью. Передача указателей вместо значений может уменьшить использование флэш-памяти.
Нет, основная проблема в том, что Arduino - это не ПК, поэтому у него нет FPU. И мне непонятно, зачем вообще нужна плавающая точка.
При этом передача структур по значению - плохая практика на всех компьютерах, даже на ПК.
Спасибо, код отлично работает. Что касается скорости, я провел тест, используя функцию arduino millis (). На обработку числа 1233.3456789 неоднократно уходило 25 мс. С огромным числом (1233123123134,3456789) потребовалось 26 мс. Я согласен, что это кажется несколько медленным, но у меня очень мало опыта в подобных вещах. Это медленно? и как это можно было улучшить?
Какое отношение имеет BCD к плавающей запятой? Зачем нужна плавающая точка? Если вам нужна плавающая точка, Arduino - ужасный выбор, учитывая, что AVR - это устаревшая 8-битная архитектура без FPU.