Печать с округлением в сторону нуля вместо округления до ближайшего
При печати с одним знаком после запятой Serial.println()
Arduino округляет числа вроде 123.89
до «123.9». Есть ли способ изменить режим округления? По некоторым причинам я хочу, чтобы значение округлялось в сторону нуля (т. е. усекалось) до одного знака после запятой, например 123.89
→ «123.8».
вот код
void setup() {
Serial.begin(9600);
}
void loop()
{
float A = analogRead(A0);
A = (A/1023)*5.0;
Serial.print("A = ");
Serial.println(A,3);
float B = A*10;
Serial.print("A * 10 = ");
Serial.println(B,1);
delay(1000);
}
выход
A = 2.473
A * 10 = 24.7
A = 2.488
A * 10 = 24.9
A = 2.517
A * 10 = 25.2
A = 2.527
A * 10 = 25.3
A = 2.507
A * 10 = 25.1
вместо этого я хочу, чтобы вывод был таким
A = 2.473
A * 10 = 24.7
A = 2.488
A * 10 = 24.8
A = 2.517
A * 10 = 25.1
A = 2.527
A * 10 = 25.2
A = 2.507
A * 10 = 25.0
это возможно?
@Peouse Dutta, 👍2
Обсуждение2 ответа
Лучший ответ:
Простой трюк для округления числа до одного десятичного знака (в «режиме обрезания») — умножить его на 10, преобразовать в целое число и снова разделить на 10.0f
:
float myVal = 123.89f;
myVal = (long)(myVal * 10) / 10.0f;
// = ((long)1238.9) / 10.0f
// = 1238 / 10.0f
// = 123.8
Затем вы можете распечатать этот измененный номер на последовательном мониторе.
Этот метод можно также обобщить для других значений точности. Для 2 знаков после запятой нам просто нужно умножить на 10^2 (100) и разделить на 10^2. Макрос будет таким:
#define TRUNCATE(val, numDecimalPlaces) \
(float)(((long)((double)(val) * pow(10, (numDecimalPlaces) * 1.0f))) / (pow(10, (numDecimalPlaces)* 1.0f)))
Примечание: вам не нужно фактически динамически вычислять pow(10, numDecimalPlaces)
, вы также можете создать макросы для каждой десятичной точности от 1 до x
и просто предварительно вычислить это значение (100, 1000, 10000, ..).
Того же самого можно добиться, используя стандартную библиотечную функцию trunc
из math.h
:
#define TRUNCATE_TO_ONE(val) \
(trunc((val) * 10.0f) / 10.0f)
См. http://www.cplusplus.com/reference/cmath/trunc/.
Вот небольшая зарисовка, в которой это продемонстрировано:
#include <Arduino.h>
#define TRUNCATE(val, numDecimalPlaces) \
(float)(((long)((double)(val) * pow(10, (numDecimalPlaces) * 1.0f))) / (pow(10, (numDecimalPlaces)* 1.0f)))
#define TRUNCATE_TO_ONE(val) \
(trunc((val) * 10.0f) / 10.0f)
void printValueFormatted(float value, int numDecimalPlaces) {
//format correctly
char buf[20];
char* res = dtostrf(value, sizeof(buf)-1, numDecimalPlaces, buf);
//skip over empty chars
while(*res == ' ')
res++;
//print buffer
Serial.println(res);
}
void setup(void)
{
Serial.begin(115200);
}
void loop(void)
{
float myVal = 123.8978f;
printValueFormatted(myVal, 4);
printValueFormatted(TRUNCATE(myVal, 1), 1);
printValueFormatted(TRUNCATE(myVal, 2), 2);
printValueFormatted(TRUNCATE(myVal, 3), 3);
printValueFormatted(TRUNCATE(myVal, 1), 4);
printValueFormatted(TRUNCATE(myVal, 2), 4);
printValueFormatted(TRUNCATE(myVal, 3), 4);
delay(1000);
}
Печатные издания
123.8978
123.8
123.89
123.897
123.8000
123.8900
123.8970
У меня возникли некоторые проблемы при использовании этого фрагмента кода. Я использую последовательную печать для печати некоторых аналоговых значений с выводов A0 и A1 платы Arduino Uno и делю значения друг на друга, чтобы получить отношение. Например: A = A0
и B = A1
, затем C = A/B
и печатаю значения C * 100
на последовательном мониторе. Все работало отлично, пока я не сделал значение B очень низким. Применение этого кода дает мне отрицательные значения отношения, а без него он дает идеальные значения, округленные до ближайшего. Есть ли что-нибудь еще, чтобы преодолеть эту проблему?, @Peouse Dutta
@PeouseDutta int
— это 2-байтовое целое число с диапазоном от -32K до +32K. Может оказаться слишком маленьким для вас, если вы внезапно умножите на 100. Я обновил код, чтобы преобразовать его в long
., @Maximilian Gerhardt
Обратите внимание, что pow()
требует больших затрат, если у вас нет FPU., @Edgar Bonet
Тогда что ты предлагаешь, @Эдгар Бонет?, @Peouse Dutta
Я уже говорил об этом выше. Pow нужен только для вычисления 10^x, просто предварительно вычислите его., @Maximilian Gerhardt
Я провел небольшой тест, и оказалось, что вызов pow()
с константными аргументами времени компиляции оптимизируется gcc в константу. Так что с pow()
все должно быть в порядке. Проверьте сгенерированную сборку, если сомневаетесь., @Edgar Bonet
Используйте данную [функцию] для вашей проблемы. Эта функция работает по предопределенному [алгоритму].
int roundTowardZero(float number, int decimal_place) {
float round_number = round(number);
if(round_number > number) round_number = round_number - 1;
float deci = (number - round_number) * pow(10, decimal_place);
float round_decimal = round(deci);
if(round_decimal > deci) round_decimal = round_decimal - 1;
round_decimal = round_decimal / pow(10, decimal_place);
total_number = round_number + round_decimal;
return total_number;
}
Поместите эту функцию выше int setup()
нравится:
int roundTowardZero( ... ) {
...
}
void setup() {
...
}
void loop() {
...
}
Синтаксис функции выглядит следующим образом: roundTowardZero([Введите десятичное значение], [Введите значение разряда округления])
Пример:roundTowardZero(123.89, 1) //Выход: 123.8
Пожалуйста, оставьте комментарий ниже, если вы обнаружили какую-либо проблему или столкнулись с какой-либо ошибкой.
- Float печатается только 2 десятичных знака после запятой
- Отправка и получение различных типов данных через I2C в Arduino
- Как вызвать функции C из скетча ардуино?
- max7219 связанный дисплей, показывающий зеркальный текст
- Пиринговая коммуникация
- Запуск С для ардуино
- Как связаться с ESP8266 ESP01, отправив данные через программный сериал на Arduino Uno?
- Прерывание ардуино при смене контакта
Мы не сможем сказать вам, что вы делаете неправильно, если мы не знаем, что именно вы делаете., @Ignacio Vazquez-Abrams
Это не последовательный монитор. Это последовательные выходные операторы в вашем скетче или, что более вероятно, сам формат с плавающей точкой. Возможно, вы могли бы рассмотреть использование
double
вместоfloat
. Но также неясно, что печатаемое значение уже не является наиболее точным десятичным приближением значения, хранящегося внутри. **Без вашего кода это невозможно.**, @Chris StrattonЯ написал это, я отслеживал некоторые аналоговые значения и выводил их на последовательный монитор, например, пусть значения будут такими, как 2,59, и когда я выводил их с точностью до одного десятичного знака, они округлялись до 2,6 по каким-то причинам, мне нужно, чтобы значение было таким, как 2,5., @Peouse Dutta
Аналоговые значения в Arduino (и почти во всех микроконтроллерах) являются целыми числами, поэтому вы сделали что-то, чтобы превратить их в значения с плавающей точкой, и что-то еще, чтобы вывести их на экран. Вы должны показать свой код, отредактировав его в тексте вашего вопроса. Если вы этого не сделаете, вопрос, вероятно, будет закрыт и удален с сайта. (Кроме того, ожидается, что аналоговые значения будут показывать вариацию шума), @Chris Stratton
проверь @ChrisStratton, @Peouse Dutta
@IgnacioVazquez-Abrams, @Peouse Dutta
Итак, вы хотите, чтобы вывод был усечен, а не округлен? 2,59 как 2,5 вместо 2,6?, @Craig
да сокращено @Craig, @Peouse Dutta
То, что вы описываете, ни в коем случае не является значением, которое "точно до одного знака после запятой". Ваша главная проблема здесь в том, что вы не смогли четко сформулировать, что вам нужно значение, которое **намеренно неточное**. У вас есть какие-либо фактические обоснования для **искажения** ваших данных таким образом???, @Chris Stratton
Обратите внимание, что большинство десятичных чисел не могут быть точно представлены в виде чисел с плавающей точкой. Например, если вы введете
float x = 23.40;
, компилятор округлит это число до ближайшего представимого числа с плавающей точкой, которое, как оказалось, имеет _точное_ значение 23.3999996185302734375. Если вы усечете это до одного десятичного знака, то23.40
будет напечатано как «23.3»!, @Edgar Bonet