PDA

Просмотр полной версии : Как округлить значение в STRING?



lava_02
04.05.2013, 19:46
Необходимо чтобы при преобразовании REAL_TO_STRING, значение переменной
округлялось до второго знака после запятой. Т.е. REAL_TO_STRING(XXX) при значении "X",
например 1.111111, значение STRING было бы 1.11, а не 1.111111. Как это осуществить?

capzap
04.05.2013, 20:03
попробуйте так

lava_02
04.05.2013, 20:32
попробуйте так
Спасибо, помогло! А как быть если foo=0.111111? Выдаёт в STRING ".11", а хотелось чтобы было "0.11"?

capzap
04.05.2013, 20:59
проверяйте, если точка занимает первое знакоместо то добавляете ноль в начало строки, это Вам в качестве домашнего задания

lava_02
04.05.2013, 21:45
проверяйте, если точка занимает первое знакоместо то добавляете ноль в начало строки, это Вам в качестве домашнего задания
Не хотелось бы это в ручную делать, в зависимости от значения переменной (1.111111 или 0.111111), хотелось бы чтобы результат был всегда 1.11 или 0.11, без ручного добавления "0" в начало строки, как Вы пишите...
Это возможно сделать?

Yegor
04.05.2013, 22:13
PROGRAM PLC_PRG
VAR
pi: REAL := 3.141592;
str: STRING;
END_VAR

str := REAL_TO_STRING(REAL_TO_INT(pi * 100) / REAL#100);

Но если для визуализации, то там всё проще. См. справку.

lava_02
04.05.2013, 22:35
PROGRAM PLC_PRG
VAR
pi: REAL := 3.141592;
str: STRING;
END_VAR

str := REAL_TO_STRING(REAL_TO_INT(pi * 100) / REAL#100);

Но если для визуализации, то там всё проще. См. справку.

Нет, не для визуализации. Для SysLibFile нужно. Ваш пример выдаёт
'6.5e-1' при 0.6462781, а надо "0.64"...

capzap
04.05.2013, 22:57
Не хотелось бы это в ручную делать, в зависимости от значения переменной (1.111111 или 0.111111), хотелось бы чтобы результат был всегда 1.11 или 0.11, без ручного добавления "0" в начало строки, как Вы пишите...
Это возможно сделать?

А кто говорил про ручное добавление, команда FIND, изучайте принцип работы и вникайте в моё изложение логической цепочки

rovki
04.05.2013, 22:58
0.6462781, а надо "0.64"...
Строго говоря это не округление :rolleyes:

capzap
04.05.2013, 23:08
Строго говоря это не округление :rolleyes:

соглашусь, тогда так

bar := INT_TO_STRING(REAL_TO_INT(foo*100));
bar := INSERT( bar,'.',LEN(bar)-2);
IF FIND(bar,'.')=1 THEN
bar := INSERT( bar,'0',0);
ELSIF bar = '0' THEN
bar := '0.00';
END_IF;

lava_02
05.05.2013, 00:26
соглашусь, тогда так

bar := INT_TO_STRING(REAL_TO_INT(foo*100));
bar := INSERT( bar,'.',LEN(bar)-2);
IF FIND(bar,'.')=1 THEN
bar := INSERT( bar,'0',0);
ELSIF bar = '0' THEN
bar := '0.00';
END_IF;


Никогда бы не подумал, что такая простая задача решается таким кодом. Но всё равно спасибо!

petera
05.05.2013, 02:14
Когда-то для ПЛК73 сам сталкивался с подобным вопросом.
Все уже придумано до нас. В библиотеке Oscat есть функция REAL_TO_STRF
Если не хотите подключать эту библиотеку целиком, то вот ее код.

FUNCTION REAL_TO_STRF : STRING(20)
VAR_INPUT
IN : REAL;
N : INT;
END_VAR
VAR
O: REAL;
i: INT;
END_VAR

(* LIMIT N to 0 .. 7 *)
N := LIMIT(0,N,7);
(* round the input to N digits and convert to string *)
O := ABS(in) * EXP(N* 2.30258509299405);
REAL_TO_STRF := DINT_TO_STRING(REAL_TO_DINT(O));
(* add zeroes in front to make sure sting is at least 8 digits long *)
FOR i := LEN(REAL_TO_STRF) TO N DO REAL_TO_STRF := CONCAT('0', REAL_TO_STRF); END_FOR;
(* add a dot if n > 0 *)
IF n > 0 THEN REAL_TO_STRF := INSERT(REAL_TO_STRF, '.', LEN(REAL_TO_STRF) - N); END_IF;
(* add a minus sign if in is negative *)
IF in < 0 THEN REAL_TO_STRF := CONCAT('-', REAL_TO_STRF); END_IF;
Здесь и выбор кол. знаков после запятой и работает округление для отброшенных знаков.
ЗЫ. Вариант предложенный capzap
bar := INT_TO_STRING(REAL_TO_INT(foo*100));
bar := INSERT( bar,'.',LEN(bar)-2);
IF FIND(bar,'.')=1 THEN
bar := INSERT( bar,'0',0);
ELSIF bar = '0' THEN
bar := '0.00';
END_IF;
для значения переменной <0, например (-0.6462781) выдает строку "-.65", т.е без ведущего 0.

capzap
05.05.2013, 09:50
для значения переменной <0, например (-0.6462781) выдает строку "-.65", т.е без ведущего 0.

:) С Вашей квалификацией докапываться до пустяков не серьезно
flag := foo<0.0;
bar := DINT_TO_STRING(ABS(REAL_TO_DINT(foo*100)));
bar := INSERT( bar,'.',LEN(bar)-2);
IF FIND(bar,'.')=1 THEN
bar := INSERT( bar,'0',0);
ELSIF bar = '0' THEN
bar := '0.00';
END_IF;
IF flag THEN bar := INSERT( bar,'-',0); END_IF;

Вот если бы Вы подвергли мой код временному анализу и доказали что оскатовский код выполняется гораздо быстрее, тогда другое дело

Валенок
05.05.2013, 21:16
Когда-то для ПЛК73 сам сталкивался с подобным вопросом.Все уже придумано до нас..
На ПЛК63/73 флеша нет, а на экран проще вывести через ShowReal. Формат более свободный : "Темп-ра %4.1f *С"


Вот если бы Вы подвергли мой код временному анализу и доказали что оскатовский код выполняется гораздо быстрее, тогда другое дело
Путем различных ухищрений можно сделать аналог REAL_TO_STRING примерно в 3-4 раза быстрее. Но это оптимизация по скорости кода capzap'а. Ну и неплохо бы учесть варианты входа типа 0.02 :)

capzap
05.05.2013, 22:09
Ну и неплохо бы учесть варианты входа типа 0.02 :)
конструктивно :)

flag := foo<0.0;
bar := DINT_TO_STRING(ABS(REAL_TO_DINT(foo*100)));
IF LEN(bar)<2 THEN bar := INSERT( bar,'0',0); END_IF;
bar := INSERT( bar,'.',LEN(bar)-2);
IF FIND(bar,'.')=1 THEN
bar := INSERT( bar,'0',0);
ELSIF bar = '0' THEN
bar := '0.00';
END_IF;
IF flag THEN bar := INSERT( bar,'-',0); END_IF;

Smith2007
06.11.2013, 06:46
Потребовалось округление до 1 знака после запятой. Алгоритм должен быть наименее ресурсоемкий.
На Ваш суд:


FUNCTION REAL_TO_STRINGF : STRING[20]
VAR_INPUT
Real_X: REAL;
END_VAR
VAR
pt_in: POINTER TO BYTE;
pt_out: POINTER TO BYTE;
instr: STRING[20];
strlen, i: INT;
END_VAR

IF ABS( Real_X ) < 0.1 THEN
REAL_TO_STRINGF := '0.0';
RETURN;
END_IF;
instr := REAL_TO_STRING( Real_X );
strlen := LEN(instr);
pt_in := ADR(instr);
pt_out := ADR( REAL_TO_STRINGF );
i:= 1;
WHILE i <= strlen DO
IF pt_in^ = 46 THEN
pt_out^ := pt_in^;
IF i = strlen THEN
pt_out := pt_out + 1;
pt_out^ := 48;
ELSE
pt_in := pt_in + 1;
pt_out := pt_out + 1;
pt_out^ := pt_in^;
END_IF;
EXIT;
END_IF;
pt_out^ := pt_in^;
pt_in := pt_in + 1;
pt_out := pt_out + 1;
i := i +1;
END_WHILE
pt_out := pt_out + 1;
pt_out^ := 0;

Валенок
06.11.2013, 14:55
IF ABS( Real_X ) < 0.1 THEN
REAL_TO_STRINGF := '0.0';
RETURN;
END_IF;
?
0.07 => 0.1

Smith2007
06.11.2013, 15:19
Да, знаю. Это не "округление" в математическом смысле, а просто отбрасываем все, что после десятых. Для поставленной задачи это более чем достаточно.
Мне нужно передать информацию по смс более точную чем сейчас я передаю в целых. :)
На основе этой информации ПЛК не производит управление. Чисто визуализация.

petera
06.11.2013, 15:41
Да, знаю. Это не "округление" в математическом смысле, а просто отбрасываем все, что после десятых. Для поставленной задачи это более чем достаточно.
Мне нужно передать информацию по смс более точную чем сейчас я передаю в целых. :)
На основе этой информации ПЛК не производит управление. Чисто визуализация.
OSCAT-овская функция REAL_TO_STRF, ее код выше http://www.owen.ru/forum/showthread.php?t=15029&p=110112&viewfull=1#post110112, как раз таки производит именно математическое округление, а не отбрасывание знаков после запятой.

Smith2007
06.11.2013, 16:02
Я посмотрел. Согласен, но ее ресурсоемкость не соизмерима. При всем, что нет таких требований. Во всем разумная необходимость д.б.

capzap
06.11.2013, 16:15
Я посмотрел. Согласен, но ее ресурсоемкость не соизмерима. При всем, что нет таких требований. Во всем разумная необходимость д.б.
могу сказать что использование while может привести к перегрузке контроллера, так что совсем не показательно Вам разговоры разговаривать о какой то там ресурсоемкости

Валенок
06.11.2013, 16:38
Потребовалось округление до 1 знака после запятой.

Это не "округление" в математическом смысле, а просто отбрасываем все,
Вы бы тоже определились

capzap
06.11.2013, 17:37
Потребовалось округление до 1 знака после запятой. Алгоритм должен быть наименее ресурсоемкий.
На Ваш суд:


FUNCTION REAL_TO_STRINGF : STRING[20]
VAR_INPUT
Real_X: REAL;
END_VAR
VAR
pt_in: POINTER TO BYTE;
pt_out: POINTER TO BYTE;
instr: STRING[20];
strlen, i: INT;
END_VAR

IF ABS( Real_X ) < 0.1 THEN
REAL_TO_STRINGF := '0.0';
RETURN;
END_IF;
instr := REAL_TO_STRING( Real_X );
strlen := LEN(instr);
pt_in := ADR(instr);
pt_out := ADR( REAL_TO_STRINGF );
i:= 1;
WHILE i <= strlen DO
IF pt_in^ = 46 THEN
pt_out^ := pt_in^;
IF i = strlen THEN
pt_out := pt_out + 1;
pt_out^ := 48;
ELSE
pt_in := pt_in + 1;
pt_out := pt_out + 1;
pt_out^ := pt_in^;
END_IF;
EXIT;
END_IF;
pt_out^ := pt_in^;
pt_in := pt_in + 1;
pt_out := pt_out + 1;
i := i +1;
END_WHILE
pt_out := pt_out + 1;
pt_out^ := 0;




А как такой вариант, вместо Вашего кода
REAL_TO_STRINGF := REAL_TO_STRING(INT_TO_REAL(TRUNC(Real_X * 10.0))/10.0);

Smith2007
06.11.2013, 18:59
Тоже вариант. Просто еще со студенческой скамьи помню, что операции умножения и деления (особенно чисел с плавающей точкой) довольно трудоемки для процессоров. Позже еще появились математические сопроцессоры, которые и выполняли данные операции. А While это всего лишь целочисленный счетчик.
(Ну это я так думаю, хотя могу и ошибаться. Давно с IT и программированием не сталкивался).

capzap
06.11.2013, 19:15
не трудоемки а занимают больше процессорного времени, но если реал в четыре раза превышает по времени простой счет, то сравните сколько Вы делаете инструкций и сколько в моей строчке даже помноженной на четыре

Smith2007
06.11.2013, 19:45
Практически одинаково по времени получилось
https://lh4.googleusercontent.com/-59_tl7GQGIc/Unpj1Gf8ZtI/AAAAAAAADcY/Xql3MezTf-I/w967-h693-no/testround.jpg

capzap
06.11.2013, 19:56
если это сравнение с моим кодом
ну и раз нет разницы зачем "платить больше", чем меньше объем кода тем меньше ошибок:)
а так то Вы сделайте штук сто таких вычислений, по одному значению ни кто ни когда не судит