столкнулся с непонятным. мные головы, объясните пожалуйста

Тема в разделе "Arduino & Shields", создана пользователем A.T.M, 5 окт 2014.

  1. A.T.M

    A.T.M Нерд

    вот скетч:

    void setup()
    {
    Serial.begin(9600);
    }
    void loop()
    {
    delay(5000);
    float b = pow(10,3);
    int c = b;
    Serial.println(c);
    Serial.println(b);
    while(1);
    }

    вот другой скетч:

    void setup()
    {
    Serial.begin(9600);
    }
    void loop()
    {
    delay(5000);
    float b = pow(10, Serial.available()); //ввожу значение 000
    int c = b;
    Serial.println(c);
    Serial.println(b);
    while(1);
    }

    в обоих случаях float b = 1000.00, а int c получается разным. Почему так?
     
  2. NR55RU

    NR55RU Гик

    Serial.available() возвращает количество байт готовых для чтения, если вы используете Монитор порта, то скорее всего монитор порта шлет вместе с вашими 3-мя символами еще и символы новой строки или еще какие, завершающие, в зависимости от настройки порта, что разумеется так же считается как доступными для чтения.
    И ваш int во втором случае получается больше чем в первом :)

    На тему монитора порта можете почитать тут.
     
  3. geher

    geher Гуру

    Если во втором случае воспользоваться вызовом
    Serial.println(b,8);
    то станет понятно, что на самом деле значение b не совсем 1000.00, а 999.9998 и еще некоторое количество цифр после запятой. Просто при выводе было выполнено округление.
    Все дело в двух вещах: функции pow, которая считает в плавающей точке при помощи хитрой формулы с очень плохой точностью, и оптимизаторе, который разным образом вызывает означенную функцию для целочисленной константы и неизвестного заранее значения, возвращаемого функцией, или даже вызывает разные функции, или вызов для константы превращает в последовательность умножений (кто его, оптимизатор данного конкретного компилятора, знает).

    Простое присваивание целочисленной переменной значения с плавающей точкой
    int c = b;
    приводит к простому отбрасыванию дробной части. В результате и получаем 999.
    Если хочется чего-то более точного, то лучше воспользоваться функцией round, которая выполняет правильное округление.
    int c = round(b);
    А еще лучше, если речь идет о возведении целого числа в целую степень, и необходимо достижение абсолютной точности, вместо неточной функции pow воспользоваться циклом.
    int c=1;
    int avail=Serial.available()
    for(int j=0;j<avail;j++) c*=10;
    При очень больших степенях это, конечно, будет очень неэффективно, и дело может даже закончиться переполнением целочисленной переменной, но для таких случаев можно перед вычислением проверять значение степени и выбирать, что будет вызываться, цикл или pow.
     
    Последнее редактирование: 5 окт 2014
    ИгорьК и A.T.M нравится это.
  4. A.T.M

    A.T.M Нерд

    Спасибо за объяснение и за советы:)