Как разбить число с плавающей запятой на отдельные цифры?

Я помогаю другу с небольшим проектом по электронике, используя микроконтроллер PIC 16F877 (A), который я программирую с помощью mikroC.

Я столкнулся с проблемой, заключающейся в том, что у меня есть число с плавающей запятой в переменной, скажем, например, 1234.123456, и мне нужно разделить его на переменные, содержащие каждое отдельное число, поэтому я получаю Char1 = 1, Char2 = 2 и т. д. и т. д. для отображения на ЖК. Число всегда будет округляться до 3 или 4 знаков после запятой, поэтому необходимо отслеживать расположение десятичной точки.

Любые советы о том, как получить этот раскол, будут очень благодарны.

Ответы (3)

Есть множество способов сделать это. Вы можете обнаружить, что ваш компилятор имеет библиотечную функцию, которая сделает это за вас. Это может быть возможно с:

  • спринтф() / спринтф()
  • dtostrf()
  • дтоа()

В качестве альтернативы, не так уж сложно написать свою собственную процедуру, чтобы сделать это. Это всего лишь случай, когда сначала нужно определить, сколько цифр перед запятой, разделить его на 10 столько раз, а затем повторно взять целую часть при умножении на 10, убедившись, что вы добавляете десятичную точку в нужное место.

Таким образом, в псевдокоде это может выглядеть примерно так:

If the value < 0.0:
    Insert - into string
    Subtract value from 0.0 to make it positive.
While the value <= 10.0:
    Divide by 10
    Increment decimal counter
For each digit of required precision:
    Take the integer portion of the value and place it in the string
    Subtract the integer portion from the value
    Decrement decimal counter
    If decimal counter is 0:
        Insert decimal point
    Multiply the value by 10.

Используйте спринтф(). Он работает точно так же, как printf(), но «печатает» в строку (также известную как массив символов).

сделать: char stringvar[10]; sprintf(stringvar, "%9.4f", floatvar);

Не во всех библиотеках часть vsprintf с плавающей запятой скомпилирована для экономии места. Особенно на 8-битных компиляторах.
@Majenko: Это может быть правдой, но если он обрабатывает числа с плавающей запятой, есть большая вероятность, что он уже использует некоторые библиотеки с плавающей запятой.
Библиотеки математики с плавающей запятой полностью отделены от обработки с плавающей запятой printf. Например, взгляните на avr-libc Arduino. Имеет полную поддержку с плавающей запятой, но не может печатать с плавающей запятой, потому что этот код был специально исключен, поскольку он невероятно велик.
Если он использует библиотеки с плавающей запятой, у него, вероятно, достаточно места для кода, и он может также включить библиотеку printf.
@Austin Дело не в том, чтобы «включить» «библиотеку printf», а в том, имеет ли встроенная библиотека C поддержку операций с плавающей запятой в функции vsprintf. Вы ничего не можете «включить», чтобы изменить это, кроме использования другой библиотеки C.
Я не имею в виду буквальное #include, но связывание с ним, и под «библиотекой printf» я подразумеваю ту, в которой есть vsprintf, но printf — это наиболее распространенное ее использование. Я никогда не видел vsprintf, который не может также выполнять числа с плавающей запятой.

На небольшом микроконтроллере без аппаратной поддержки с плавающей запятой мы должны делать как можно меньше математических вычислений с плавающей запятой, и если вам действительно не нужно семейство функций printf, старайтесь избегать его, потому что он сильно раздувает и замедляет код.

Я предлагаю преобразовать число с плавающей запятой в целое число, сначала умножив число с плавающей запятой на 1000,0 (при условии, что вам нужны три десятичных знака), а затем преобразовать его в длинное целое число, округлив по мере необходимости. Если вы будете отображать результат на 7-сегментном или точечно-матричном ЖК-дисплее, то я думаю, что этот формат идеален.

Предположим, что число с плавающей запятой может быть в диапазоне от 0 до 999,999 (отрицательно, если отрицательное, сохранить знак для отображения позже). Соответствующее длинное целое имеет диапазон от 0 до 999999. Мы будем преобразовывать число, начиная со старшей значащей цифры.

Псевдокод:

dig6 = -1  // Init MSDigit
while number >= 0
   number = number - 100,000
   dig6 = dig6 + 1
number = number + 100,000  // Restore number, Dig 6 is done

dig5 = -1
while number >= 0
   number = number - 10,000
   dig5 = dig5 + 1
number = number + 10,000 // number can be switched to 16-bit here for speed

... dig4 and 3 in similar fashion

dig2 = -1
while number >= 0
   number = number - 10
   dig2 = dig2 + 1
dig1 = number + 10

На данный момент у вас есть все шесть цифр, сохраненных в байте каждая, и сохранен знак минус. Если вы используете 7-сегментный ЖК-дисплей, передайте цифры функции 7-сегментного кодировщика перед записью на ЖК-дисплей. Если вы используете матричный дисплей с последовательным интерфейсом, добавьте 0x30 к каждой цифре для кодировки ASCII. Нам также нужно запомнить десятичную точку между dig4 и dig3.

Этот алгоритм довольно быстр, так как в нем нет умножения и деления. Я использовал его на крошечных 4-битных микроконтроллерах с хорошими результатами.