Почему простейший код по разному считает на 2560 и 328p?
- Войдите на сайт для отправки комментариев
Господа, у меня похоже приступ кретинизма. Есть программа, отлаженная и работающая на Mega2560.
Сделал версию для 328p (ProMini 5v 16Mhz, если это имеет значение). Код рассчета НЕИЗМЕННЫЙ, это важно. Входящие данные - идентичные. Задача кода - разложить входящую переменную float на символы для отображения на семисегментном индикаторе через max7219.
Сам код:
//положительное число до 99, один знак после запятой. pp_val=p_val*10; //4 сегмент lc.setRow(disp,value_z[0],0x0); //3 сегмент if(pp_val/100 == 0) lc.setRow(disp,value_z[1],0x0);else lc.setDigit(disp,value_z[1],((pp_val/100)%10),false); //2 сегмент lc.setDigit(disp,value_z[2],((pp_val/10)%10),true); //1 сегмент lc.setDigit(disp,value_z[3],(pp_val%10),false);
pp_val - временная переменная типа long;
p_val - входящее значение типа float;
Столкнулся я с тем, что имея на входе p_val=0.40, на mega мне индикатор показывает 0.4, как и должен, а на 328p - 0.3
Проблема явно в разложении на разряды, потому как вот такой вот код (отображение с 2 знаками после запятой):
// число от -9 до 9, два знака после запятой. if(p_val<0) { minus_sg=1; pp_val=p_val*100; pp_val=~pp_val+1; } else { pp_val=p_val*100; } if((pp_val/1000) == 0) { if(minus_sg) lc.setChar(disp,value_z[0],'-',false); else lc.setChar(disp,value_z[0],' ',false); // 4 сегмент } else lc.setDigit(disp,value_z[0],((pp_val/1000)%10),false); lc.setDigit(disp,value_z[1],((pp_val/100)%10),true); // 3 сегмент lc.setDigit(disp,value_z[2],((pp_val/10)%10),false); // 2 сегмент lc.setDigit(disp,value_z[3],(pp_val%10),false); // 1 сегмент
На 328p выводит 0.39 при входящей опять же в 0.40, что подтверждает разную математику. Косяк вылезает на делении, но почему-то только на промини, на меге все корректно. Mega на этом же коде отображает 0.40, как и должна.
Код идентичен, данные на входе идентичны - оба контроллера получают эти 0.40 из одного источника, я проверил. Чуть не свихнулся, впихивая отдадочную печать везде, пока не дошел до функции отображения.
Похоже что округляют при делении они по разному. Есть мысли, как это исправить, сохранив общий код? И вообще, почему оно так происходит?
Прошу перенести тему в Программирование, если можно - ошибся. :(
Копания с отладкой дальше показали, что корень зла первого кода вот в этой строчке:
pp_val=p_val*10;
ПроМини умножает 0.40 на 10, и получает на выходе 3. :-/
Цитата из Serial:
Вот КАК?
Т.е. вот (примерно) так - на разных мегах разные nFloat10?
Уважаемый, а давайте сначала проведем умножение так float pp_val_float=p_val*10.; а затем проведем ее преобразование pp_val=(long) pp_val_float; Где-то здесь собака порылась...
Т.е. вот (примерно) так - на разных мегах разные nFloat10?
Ваш код работает корректно:
p_val:0.40;
Уважаемый, а давайте сначала проведем умножение так float pp_val_float=p_val*10.; а затем проведем ее преобразование pp_val=(long) pp_val_float; Где-то здесь собака порылась...
Нуль эмоций:
Я не знаю, имеет значение или нет - мой код находится внутри функции, при этом p_val - входящий параметр типа float, передающийся в функцию в качестве одного из параметров, а pp_val объявляется внутри функции.
Update:
А вот пример умножения на 100:
Мда, ну тогда только round().
Мда, ну тогда только round().
С ним работает корректно, да:
Результат:
Чечако, ничего удивительного.
Тип float - это не точные цифры, а приблизительные. Float 0.4 во внутреннем представлении легко может быть и 0.400001 и 0.39999. В одном случае при целочисленном умножении вы получите 0.4, а в другом 0.3
Чечако, ничего удивительного.
Тип float - это не точные цифры, а приблизительные. Float 0.4 во внутреннем представлении легко может быть и 0.400001 и 0.39999. В одном случае при целочисленном умножении вы получите 0.4, а в другом 0.3
Почему тогда на меге я все утро вижу 0.4, а на 328p 0.3? Был бы рандом, оно бы проскакивало и там и там. А оно стабильно, как минимум на уровне нескольких минут по логам и нескольких часов по глазам. :)
Ну и главный вопрос - использование round везде в коде исправит проблему, или тоже способно неприятно удивить?
Вытащено из интернета, но сам всегда делаю так же:
Почему тогда на меге я все утро вижу 0.4, а на 328p 0.3? Был бы рандом
разве я где-то написал, что там рандом? Я говорил, что внутреннее представление числа не точное, но эта "неточность" в заданных условиях всегда одинакова, никакого рандома нет. Конкретное значение будет зависеть от представления числа в регистрах данного процессора. Поэтому на одной атмеге вы всегда получаете 0.3, а на другой 0.4
разве я где-то написал, что там рандом? Я говорил, что внутреннее представление числа не точное, но эта "неточность" в заданных условиях всегда одинакова, никакого рандома нет. Конкретное значение будет зависеть от представления числа в регистрах данного процессора. Поэтому на одной атмеге вы всегда получаете 0.3, а на другой 0.4
Понял, спасибо. А эта "неточность" - она является характеристикой всей серии микроконтроллеров, или каждого конкретного чипа в ней?
Т.е. скажем получать 0.4 я буду на любой 2560 и 0.3 на любой 328p, или каждый чип надо смотреть индивидуально?
В принципе тут можно сделать и еще один вывод - к преобразованию типов переменных стоит относится с бо..ольшой осторожностью!
Понял, спасибо. А эта "неточность" - она является характеристикой всей серии микроконтроллеров, или каждого конкретного чипа в ней?
это зависит от реализации библиотек работы с плавающей точкой. Эта реализация может быть одинаковой для целого класса контроллеров. или иметь особенности для разных моделей. Но в любом случае о конкретных экземплярах чипов речи не идет, у пачки чипов из одной партии все должно быть одинаково.
Понял, спасибо. А эта "неточность" - она является характеристикой всей серии микроконтроллеров, или каждого конкретного чипа в ней?
При округлении следует перед операцией прибавлять половину единицы младшего разряда. В данном случае это 0.005.
В данном случае "неточность" - качество, присущее используемое Вами способу вычисления.
При округлении следует перед операцией прибавлять половину единицы младшего разряда. В данном случае это 0.005.
Этот вариант я знаю, да. Но меня смутило именно то, что вынесено в заголовок - что код корректно работает на 2560, и не работает на 328p. :) Я исходил из того, что подобные операции или работают корректно на всех arduino, или опять же не работают на всех. Избирательный характер мне в голову не пришел. :(
Видите ли, когда в программе есть ошибка, то (в среднем) трудно предсказать заранее, в каких случаях она проявится.
В пнринципе, Вы выяснили, что для 328 и 2560 используется немного отличающаяся математика. Почему? Где? Зачем? И вообще - умышленно это сделано иои случайно так оказалось, можно, конечно, выяснить, покопавшись в исходниках. Вы готовы потратить на это свое время?
Можно найти массу подобных вопросов. Например, ЗАЧЕМ char сделан знаковым для 8-разрядных контроллеров и беззнаковым - для 32-разрядных? Стандарт Си допускает и то и другое, но зачем в разных моделях Ардуино этот вопрос решен по-разному?
В конце концов, писать программы надо так, чтобы подобные различия не сказывались на результатах работы программы, а все остальное - пустое любопытство.