просчет с плавающей запятой, когда переменная int32_t делится на 10

я хочу спросить, как получить правильное значение float из uint32_t или переменная long делится на 10??

long var3 = 1139399025;
int a = 3;
int b = 10;
void setup() {
  Serial.begin(9600);
  Serial.println((float)a/b,2);
  Serial.println((float)var3/b,2);
}
void loop() {
}

результат

0.3
113939904.00

выдает 113939904.00 вместо 113939902.50, пожалуйста, как это исправить?

, 👍1

Обсуждение

каково значение var3, когда оно преобразуется в число с плавающей запятой?, @jsotola

длинная переменная3 = 1139399025;, @mukhlas

Поплавки имеют только ограниченную точность, см. https://cdn.arduino.cc/reference/en/language/variables/data-types/float/. Вы получаете лучшую точность, используя двойник, но только на 32-битном Arduino (Due и т. Д.). Однако некоторая потеря точности все равно будет., @6v6gt

Я использую ардуино мега 2560, @mukhlas

Отвечает ли это на ваш вопрос? [Серийный номер, числа, отправленные как с плавающей запятой, и символы отличаются на уровне 1e-8](https://arduinoprosto.ru/q/8872/serial-numbers-sent-as-floating-point-and-characters-are -различный-на-1e-8-уровневом), @the busybee


1 ответ


2

1139399025 не является числом с плавающей запятой: наибольшее нечетное целое число, может быть точно представлен как float 224 − 1 = 16777215. Тогда, когда var3 равно приведено к float, оно округляется до ближайшего числа с плавающей запятой, что это 1139399040. Это совсем небольшая ошибка, особенно учитывая что в этом диапазоне только кратные 128 могут быть точно представлены в виде чисел с плавающей запятой.

Деление 1139399040 на 10 дает 113939904, что является числом с плавающей запятой. Деление с плавающей запятой дает точный результат, который печатается. Действительно, операции с плавающей запятой не всегда дают неточные результаты: иногда они точны. Однако, если вы не знаете, что вы делаете, точные результаты случаются только по счастливой случайности. В общем, ожидайте каждой операции с плавающей запятой, чтобы сделать относительную ошибку округления в диапазон 10−7.

Настоятельно рекомендуется прочитать: О чем должен знать каждый программист Арифметика с плавающей запятой.

ОБНОВЛЕНИЕ: если вы хотите напечатать значение целого числа, деленного на 10, вы можете разделить число, используя евклидово деление. Распечатать частное, затем десятичная точка, затем остаток:

Serial.print(var3 / 10);  // частное
Serial.print('.');
Serial.println(var3 % 10);  // остаток

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

,

Так что я должен делать?, @mukhlas

@mukhlas: не используйте поплавки., @Edgar Bonet

@mukhlas Или используйте double, который дает более значащие цифры, для этого конкретного случая. Но в любом случае вам нужно понимать плавающую точку, как предложил Эдгар., @the busybee

@thebusybee: микроконтроллеры AVR (точнее, их порт GCC) не поддерживают числа с двойной точностью: их тип double реализован как float32, точно так же, как float., @Edgar Bonet

Да неужели? Ну тогда не повезло. :-(, @the busybee

Спасибо вам всем :), @mukhlas