2. и большие и маленькие числа можно использовать в предложенным мною варианте, надо просто продумать алгоритм вычислений так, чтобы не было потери точности.
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
1.Да. Далее, МК быстрый ( первый комп с процом Интел был всего на 1 М), но не умеет он выполнять операции с плавающей точкой. ОЗУ мало, но не значит что не хватает.ARV писал(а):1. речь идет об AVR? не так ли? этот тип МК нельзя назвать ни быстрым, ни обладающим просторным ОЗУ
2. Можно, но зачем, если автор подтвердил, что нет потери точности.2. и большие и маленькие числа можно использовать в предложенным мною варианте, надо просто продумать алгоритм вычислений так, чтобы не было потери точности.
А Вам не приходило в голову, что десятичное число 12.075, т.е. дробь 12075 / 1000, имеющая в знаменателе число, раскладываемое на 2-ки и 5-ки, может быть просто непредставимо точно в виде дроби X / 2^N (т.е. числа, имеющего в знаменателе только двойки) с конечным N?Andrey19888 писал(а):Спасибо. Заработало. Единственный момент, что в переменной sdf по прежнему остается неточное значение, а мне с ней нужно будет работать. Как там можно поправить?
Если почитаете, то это уже опровергли, т.е. точность, для приведенных данных, не теряется.avreal писал(а):Andrey19888 писал(а):Спасибо. Заработало. Единственный момент, что в переменной sdf по прежнему остается неточное значение, а мне с ней нужно будет работать. Как там можно поправить?
Тут, как говорится и спору нет. А делить еще хуже.avr писал(а): умножение двух флоатов длится долго, использовать их в прерывании, например, становится весьма сложно
Код: Выделить всё
(float)(Gas_in-Mem_Min_Gas)/(Mem_Max_Gas-Mem_Min_Gas)*0x03ff;Код: Выделить всё
(long)(Gas_in-Mem_Min_Gas) * 0x03ff / (Mem_Max_Gas-Mem_Min_Gas);Код: Выделить всё
0x03ffUL * (Gas_in-Mem_Min_Gas) / (Mem_Max_Gas-Mem_Min_Gas);Код: Выделить всё
gas_coeff = 0x03FFUL * SCALE / (Mem_Max_Gas-Mem_Min_Gas);Код: Выделить всё
gas_coeff * (Gas_in-Mem_Min_Gas) / SCALE;Что опровергли?codenamehawk писал(а):Если почитаете, то это уже опровергли, т.е. точность, для приведенных данных, не теряется.
Просмотрел ещё раз тему, и так и не понял, что "уже опровергли".codenamehawk писал(а):Это похоже функция sprintf выдает такой результат, а переменная sdf равна 12.075Andrey19888 писал(а):Безрезультатно. На экране всеравно 12,074999. Если в sprintf(s,"%.3f",sdf); на экране правильно, но в переменной же всеравно нет
Нормально отрабатывает функция sprintf.Andrey19888 писал(а):С выводом я уже разобрался. Волновала только проблема что в переменной хранится не правильное число. Но эксперимент показал что функция sprintf не корректно отрабатывает и приходится округлять
Код: Выделить всё
#include <stdio.h>
float f = 12.075;
int main()
{
printf("%.8f\n", f);
printf("%.6f\n", f);
printf("%.4f\n", f);
printf("%.2f\n", f);
printf("%.1f\n", f);
}Единственная некорректность Вашего sprintf — так это то, что без указания разрядности, т.е. при формате %f, по умолчанию должно быть 6 знаков после запятой и он должен был округлить до 6 знаков и вывести как во второй строке выдачи выше. Но переменной не 12.075, это двоичный код...12.07499981
12.075000 ; 8-ка в 7-мом знаке после запятой пошла в округление, поэтому вместо 3 девяток вышло 3 нуля
12.0750
12.07 ; 7-ка нечётное, 5-ка округялется вниз, для 12.085 будет 12.09
12.1 ; ну тут, понятно, и 7-ка округлилась
У Вас все числа должны быть не больше 3 десятичных знаков после запятой?Andrey19888 писал(а):Разложение на 2 целых числа не вариант т.к. если ввести 23.005 в одну переменную пойдет 23, а в другую то чушь. Или как представить мелкое число, например 0.002?
Код: Выделить всё
int a = 12075;
sprintf( "%d.%03u", a / 1000, a % 1000);Код: Выделить всё
int a = 12075;
char sig;
int b;
if( a > 0) {
sig = ' '; // или '+', по вкусу
b = a;
} else {
sig = '-';
b = -a;
}
sprintf( "%c%d.%03u", sig, a / 1000, a % 1000);Во вторую уйдет 005Andrey19888 писал(а): Разложение на 2 целых числа не вариант т.к. если ввести 23.005 в одну переменную пойдет 23, а в другую то чушь.
Мелкое число 0.002Andrey19888 писал(а): Или как представить мелкое число, например 0.002?
Код: Выделить всё
TempA = (float)(In_Gas);
TempA = TempA - (float)(Mem_Min_Gas);
Delta_Gas = Mem_Max_Gas;
Delta_Gas-= Mem_Min_Gas;
TempA = TempA/Delta_Gas;
TempA = TempA * 500.0;
Код: Выделить всё
Delta_Gas = Mem_Max_Gas;
Delta_Gas-= Mem_Min_Gas;Код: Выделить всё
TempA = TempA/Delta_Gas;
TempA = TempA * 500.0;Код: Выделить всё
TempA = TempA/delta_gas_div_500;
Я имел ввиду другое - по виду имени переменные Mem_* навевают мысль о том, что это некие корректирующие коэффициенты, которые меняются редко и сохраняются где-то.clawham писал(а):это то понятно что можно некоторые коэффициенты рассчитать "до того как"
...
вынести за рамки основного цикла кусочеки всё...а я не думаю что вычитание двух интов получаются очень затратными по времени процессора....Код: Выделить всё
Delta_Gas = Mem_Max_Gas; Delta_Gas-= Mem_Min_Gas;
гораздо круче по времени занимают последние две строки - деление и умножение флоата
Попробуйте аппроксимировать кубической параболой. Что-то в духеclawham писал(а):А вот ещё на последок вторая математическая головоломка:
есть такое понятие как експонента...в авиамоелизме подразумевает нелинейную зависимость выхода от входа а хитрую кривую и с третьего(или 4-го) канала задавать процент влияния этой кривой на выход тоесть при 0 процентах вход=выходу, при 100% выход равен експоненте, а при 50% выход равен среднему между входом и експонентой)
Код: Выделить всё
y = 50 + ((x-50)^3) / 250Код: Выделить всё
y = 50 + k * ((x-50)^3) / 250 / 100 + (100 - k) * (x - 50) / 100 Код: Выделить всё
y * 65536 = 50 * 65536 + 65536 * k * ((x-50)^3) / 25000 + 65536 * (100 - k) * (x - 50) / 100
= 50 * 65536 + 65536 * k * ((x-50)^3) / 8 / 3125 + 655 * (100 - k) * (x - 50)
21 отличается от 65526 / 3125 на 0.13%
655 отличается от 65536 / 100 на 0.05%
= 3276800 + 21 * k * ((x-50)^3) / 8 + 655 * (100 - k) * (x - 50) // [1]
k1 = 21 * k
k2 = 655 * (100-k)
= 3276800 + k1 * (x - 50) * (x - 50) * (x - 50) / 8 + k2 * (x - 50) // 4 умножения + 3 сдвига на бит
= 3276800 + (x - 50) * (k1 * (x - 50) * (x - 50) / 8 + k2) // 3 умножения + 3 сдвига на бит avreal писал(а):Попробуйте аппроксимировать кубической параболой.
avreal писал(а):У Вас все числа должны быть не больше 3 десятичных знаков после запятой?Andrey19888 писал(а):Разложение на 2 целых числа не вариант т.к. если ввести 23.005 в одну переменную пойдет 23, а в другую то чушь. Или как представить мелкое число, например 0.002?
Напишите свою функцию разбора строки в число, которая будет выдавать целое число, но умноженное на 1000. Т.е. при вводе числа точку игнорировать, но считать, сколько вышло символов после десятичной точки. И если их меньше 3, то домножать на 10 столько раз, сколько не хватило.
И дальше все рассчёты вести с учётом того, что числа умножены на 1000.
При выводеupd - с отрицательными числами, пожалуй, придётся врукопашную, как-то такКод: Выделить всё
int a = 12075; sprintf( "%d.%03u", a / 1000, a % 1000);Тху!!! Уже ж 5-е число!!!!!!Код: Выделить всё
int a = 12075; char sig; int b; if( a > 0) { sig = ' '; // или '+', по вкусу b = a; } else { sig = '-'; b = -a; } sprintf( "%c%d.%03u", sig, a / 1000, a % 1000);
Конечно, везде sprintf-у буфер указывать![]()
Как я и предполагал - это ПИД регулятор. И это явление объяснимо: астатичность системы с интегратором в цепи обратной связи и обеспечивается именно колебаниями относительно расчетной точки. Настройка ПИД регулятора - тонкая штука, я хоть и руководил дипломными проектами студней специальности "Автоматика", в реале этим не занимался. Но нам хоть удалось убедить автора, что запредельная точность и головоломные Float-вычисления ни к чему.clawham писал(а): есть проблемы:
1) иногда входит в самоколебания перелетая нужное направление
Код: Выделить всё
//ждём пока вход Газ упадёт в нуль
while(Gas_in);
//теперь ждём поднятия в 1
//TCCR2=TimOn;
TCCR0 = 0x00; // таймер глушим
Timer_ext = 0; // обнуляем накопитель таймера
TCNT0 = 0;
while(!Gas_in);
//запускаем таймер
TCCR0 = TimOn;
// ждём пока вход Газ упадёт вниз
while(Gas_in);
//срочно тормозим таймер
TCCR0 = 0x00;
// расчитываем кол-во отсчетов от нуля
In_Gas = TCNT0+Timer_ext;
In_Gas = In_Gas>>1;
//ждём пока вход Гиры упадёт в нуль
while(Gyro_in);
//теперь ждём поднятия в 1
//TCCR2=TimOn;
TCCR0 = 0x00; // таймер глушим
Timer_ext = 0; // обнуляем накопитель таймера
TCNT0 = 0; // обнулили и сам таймер
while(!Gyro_in);
//запускаем таймер
TCCR0 = TimOn;
// ждём пока вход Гирo упадёт вниз
while(Gyro_in);
//срочно тормозим таймер
TCCR0 = 0x00;
// расчитываем кол-во отсчетов от нуля
In_Gyro = TCNT0+Timer_ext;
In_Gyro = In_Gyro>>1;
if((In_Gas>2200)||(In_Gyro>2200)||(In_Gyro<800))
continue;
#asm("wdr")
if(In_Gas<1050)
continue;
TempA = In_Gas;
TempB = In_Gas;
if(In_Gyro>1520)
{
// гирик скрутило вверх - первый канал увеличиваем, второй уменьшаем
TempC=In_Gyro-1520; // получили микросекунды смещения
TempA+=(TempC>>1);
TempB-=(TempC>>1);
if(TempA>2000)
{
// перелетели максимум - вернем его в минус второго канала
TempB-=TempA-2000;
}
}
if(In_Gyro<1520)
{
// гирик скрутило вниз - второй канал увеличиваем, первый уменьшаем
TempC=1520-In_Gyro; // получили микросекунды смещения
TempA-=(TempC>>1);
TempB+=(TempC>>1);
if(TempB>2000)
{
// перелетели максимум - вернем его в минус первого канала
TempA-=TempB-2000;
}
}
if(TempA<1000)
TempA = 1000;
if(TempA>2000)
TempA = 2000;
if(TempB<1000)
TempB = 2000;
if(TempB>2000)
TempB = 2000;
OCR1A = TempA;
OCR1B = TempB;