OBOE: Нужна помощь в понимании, возможная проблема преобразования типов
У меня есть алгоритм, основной целью которого является декодирование, преобразование или как там у вас строки заглавных букв (A-Z) в число. Это почти как преобразование числа базы 26 в базу 10, однако я считаю, что кодированное число базы 26 на самом деле будет включать числа, я работаю строго с заглавными буквами, я отвлекся.
У меня есть имплантация, работающая на C++11, на самом деле большая часть алгоритма контроллера была прямой копией, с небольшим изменением метода indexOf.
Версия C++11 работает так, как я ожидаю. Поэтому сначала я подумал, что новый метод indexOf неверен, но после простого теста он работает так, как ожидалось. Я сузил проблему до строки в convertTo10, где я вызываю функцию pow.
Как вы можете видеть из Serial.println () в методе convertTo10, вывод печатает именно то, что мы ожидаем, 0 и 26.
Однако выход этой функции равен 25. ЧТО?
Я прочитал документы о методе pow из библиотеки Arduino, и он принимает два float и возвращает двойной. Даже если проблема заключалась в преобразовании типа, преобразование double в int усекает число, а не округляет его. Ну, это от работы с C++.
Любая помощь в этом вопросе будет очень признательна.
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <inttypes.h>
#include <Arduino.h>
#include <avr/pgmspace.h>
#include <Wire.h>
const char _values[26] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z'
};
const int _val_len = 26;
int indexOf(char needle);
int convertTo10(String value, int len);
String x = "BA";
int len = 2;
void loop() {
Serial.print(x);
Serial.print("=> ");
Serial.println(convertTo10(x, 2));
delay(50000);
}
int convertTo10(String value, int len) {
int lastIndex = len - 1;
double output = 0;
int pos = 0;
Serial.println();
for (int i = lastIndex; i >= 0; i--) {
int r = indexOf(value[i]);
output += (r * pow((_val_len, pos));
Serial.print(" output => ");
Serial.println( output );
pos++;
}
return output;
}
int indexOf(char needle) {
int first = 0;
int last = _val_len - 1;
int mid;
do {
mid = (first + last) / 2;
if (_values[mid] == needle)
return mid;
if (_values[mid] > needle)
last = mid - 1;
else
first = mid + 1;
} while (first <= last);
return -1;
}
Для справки, вот что он выводит:
BA =>
output => 0.00
output => 26.00
25
@James Little, 👍2
Обсуждение2 ответа
слишком сложно:
int convertTo10(char *num_26, int len) {
int tmp = 0; //hold the output
int i = 0; //indexing the input
while (len--) tmp = tmp * 26 + (num_26[i++]-'A');
return tmp;
}
примечание:
"* 26" может быть дополнительно упрощен;
проверка ошибок может быть встроена здесь или где-то еще (лучше).
лучшее структурирование могло бы избавиться от одной из временных переменных.
Спасибо за оптимизированный код! Тем не менее, я надеялся, что смогу получить объяснение того, что происходит в коде, который я опубликовал? Что я сделал не так?, @James Little
То, что вы сделали неправильно, было полагаться на число с плавающей запятой, чтобы быть точным. Они почти никогда не бывают такими., @Delta_G
@Delta_G Теперь я это понимаю. Спасибо за поправку, я должен был просто подождать до утра, чтобы вернуться к этой проблеме, а не задавать глупые вопросы. Я очень ценю вашу помощь., @James Little
@dannyf Так же сильно, как я ценю это решение, а также время и усилия, которые вы потратили, чтобы помочь мне. Я должен отметить это решение как неправильное. Я проверил, что на atmel2560, скомпилированном с avr-gcc, и на Debian x64, скомпилированном с g++ по стандарту 11, этот метод всегда будет возвращать 0., @James Little
Этот код терпит неудачу, потому что " i " не инициализирован. В остальном это концептуально похоже на мой ответ. Если вы измените третью строку на int i = 0;
, это сработает., @Nick Gammon
Ваш код невероятно сложен для такой простой вещи, как преобразование числа из другой базы. Это работает (печатает 26):
const char * test = "BA";
long convertTo10 (const char * s)
{
long result = 0;
for (const char * p = s; *p; p++)
{
result *= 26; // base
result += *p - 'A'; // make zero-relative
}
return result;
} // end of convertTo10
void setup()
{
Serial.begin (115200);
Serial.println ("Starting.");
Serial.println (convertTo10 (test));
}
void loop() { }
При вводе "CBA" он печатает 1378, как и ожидалось. Размер скетча: 2300 байт памяти программы. Размер скетча уменьшается до 2236 байт, если я заставлю функцию возвращать int
(как вы это сделали), а не long
.
Вам не нужно использовать String
(на самом деле это экономит оперативную память и позволяет избежать фрагментации памяти, чтобы не использовать ее). Вам не нужно передавать длину, как с помощью char*
, так и со строкой
вы можете вывести длину. Если вы используете long
в качестве возвращаемого типа, вы можете обрабатывать большие числа.
Используя pow
, вы вводите библиотеку, которая не нужна и которая будет потреблять память программы. Используя double
, вы вводите возможные неточности, так как double не обязательно точно хранит числа. Ваш код использовал 7604 байта памяти программы, как только я исправил синтаксическую ошибку и добавил пустую функцию установки.
Кроме того, метод, который я проиллюстрировал выше, намного быстрее. Я рассчитал его при преобразовании "BA" в 4,3 мкс, тогда как метод, использующий функцию pow
, занимает 442 мкс. Таким образом, более простой метод также работает в 100 раз быстрее.
- C++ против языка Arduino?
- Как использовать SPI на Arduino?
- Какие накладные расходы и другие соображения существуют при использовании структуры по сравнению с классом?
- Ошибка: expected unqualified-id before 'if'
- Что лучше использовать: #define или const int для констант?
- Функции со строковыми параметрами
- Библиотека DHT.h не импортируется
- ошибка: ожидаемое первичное выражение перед токеном ','
Также опубликовано на [Arduino Forum](http://forum.arduino.cc/index.php?topic=515933.0)., @tttapa
В этом случае вы можете сделать возвращаемое значение равным 26 путем округления. То есть измените последнюю строку вашей функции преобразования на:
return round (output);'. Тот факт, что он возвращает 25, указывает на то, что функция " pow " не дает точного результата. Если вы увеличите количество напечатанных десятичных знаков, то увидите, что на самом деле получаете " 25.9999961853
., @Nick Gammon