OBOE: Нужна помощь в понимании, возможная проблема преобразования типов

c++

У меня есть алгоритм, основной целью которого является декодирование, преобразование или как там у вас строки заглавных букв (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

, 👍2

Обсуждение

Также опубликовано на [Arduino Forum](http://forum.arduino.cc/index.php?topic=515933.0)., @tttapa

В этом случае вы можете сделать возвращаемое значение равным 26 путем округления. То есть измените последнюю строку вашей функции преобразования на: return round (output);'. Тот факт, что он возвращает 25, указывает на то, что функция " pow " не дает точного результата. Если вы увеличите количество напечатанных десятичных знаков, то увидите, что на самом деле получаете " 25.9999961853., @Nick Gammon


2 ответа


3

слишком сложно:

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;
}

примечание:

  1. "* 26" может быть дополнительно упрощен;

  2. проверка ошибок может быть встроена здесь или где-то еще (лучше).

  3. лучшее структурирование могло бы избавиться от одной из временных переменных.

,

Спасибо за оптимизированный код! Тем не менее, я надеялся, что смогу получить объяснение того, что происходит в коде, который я опубликовал? Что я сделал не так?, @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


3

Ваш код невероятно сложен для такой простой вещи, как преобразование числа из другой базы. Это работает (печатает 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 раз быстрее.

,