Conver BCD Strings to Decimal - c++

I am looking for better ways to optimize this function for better performance, speed its targeted towards embedded device. i welcome any pointers, suggestion thanks
function converts string BCD to Decimal
int ConvertBCDToDecimal(const std::string& str, int splitLength)
{
int NumSubstrings = str.length() / splitLength;
std::vector<std::string> ret;
int newvalue;
for (auto i = 0; i < NumSubstrings; i++)
{
ret.push_back(str.substr(i * splitLength, splitLength));
}
// If there are leftover characters, create a shorter item at the end.
if (str.length() % splitLength != 0)
{
ret.push_back(str.substr(splitLength * NumSubstrings));
}
string temp;
for (int i=0; i<(int)ret.size(); i++)
{
temp +=ReverseBCDFormat(ret[i]);
}
return newvalue =std::stoi(temp);
}
string ReverseBCDFormat(string num)
{
if( num == "0000")
{
return "0";
}
else if( num == "0001")
{
return "1";
}
else if( num == "0010")
{
return "2";
}
else if( num == "0011")
{
return "3";
}
else if( num == "0100")
{
return "4";
}
else if( num == "0101")
{
return "5";
}
else if( num == "0110")
{
return "6";
}
else if( num == "0111")
{
return "7";
}
else if( num == "1000")
{
return "8";
}
else if( num == "1001")
{
return "9";
}
else
{
return "0";
}
}
Update
this is what i plan to get, for a BCD Value::0010000000000000 Decimal Result 2000

BCD is a method of encoding decimal numbers, two to a byte.
For instance 0x12345678 is the BCD representation of the decimal number 12345678. But, that doesn't seem to be what you're processing. So, I'm not sure you mean BCD when you say BCD.
As for the code, you could speed it up quite a bit by iterating over each substring and directly calculating the value. At a minimum, change ReverseBCDFormat to return an integer instead of a string and calculate the string on the fly:
temp = temp * 10 + ReverseBCDFormat(...)
Something like that.

What you call BCD is not actually BCD.
With that out of the way, you can do this:
int ConvertBCDToDecimal(const std::string& str, int splitLength)
{
int ret = 0;
for (unsigned i = 0, n = unsigned(str.size()); i < n; )
{
int v = 0;
for (unsigned j = 0; j < splitLength && i < n; ++j, ++i)
v = 2*v + ('1' == str[i] ? 1 : 0); // or 2*v + (str[i]-'0')
ret = 10*ret + v;
}
return ret;
}
Get rid of all the useless vector making and string copying. You don't need any of those.
Also, I think your code has a bug when processing strings with lengths that aren't a multiple of splitLength. I think your code always considers them to be zero. In fact, now that I think about it, your code won't work with any splitLength other than 4.
BTW, if you provide some sample inputs along with their expected outputs, I would be able to actually verify my code against yours (given that your definition of BCD differs from that of most people, what your code does is not exactly clear.)

as soon as you're optimizing function, here is different variant:
int ConvertBCDToDecimal(const std::string& str) {
unsigned int result = 0;
const std::string::size_type l = str.length();
for (std::string::size_type i = 0; i < l; i += 4)
result = result * 10 + ((str[i] - '0') << 3) + ((str[i + 1] - '0') << 2) + ((str[i + 2] - '0') << 1) + (str[i + 3] - '0');
return result;
}
note: you don't need splitLength argument, as you know that every digit is 4 symbols

Related

run-length encoding is not working with big numbers

I have a assingment were I need to code and decode txt files, for example: hello how are you? has to be coded as hel2o how are you? and aaaaaaaaaajkle as a10jkle.
while ( ! invoer.eof ( ) ) {
if (kar >= '0' && kar <= '9') {
counter = kar-48;
while (counter > 1){
uitvoer.put(vorigeKar);
counter--;
}
}else if (kar == '/'){
kar = invoer.get();
uitvoer.put(kar);
}else{
uitvoer.put(kar);
}
vorigeKar = kar;
kar = invoer.get ( );
}
but the problem I have is if need to decode a12bhr, the answer is aaaaaaaaaaaabhr but I can't seem to get the 12 as number without problems, I also can't use any strings.
try to put a repeated character when next is not numeric or end of string.
For prepare this, it needs to make number by parsing string.
about this, I recommend you to find how to convert string to integer in real time at C++.
bool isNumeric(char ch) {
return '0' <= ch && ch <= '9';
}
string decode(const string& s) {
int counter = 0;
string result;
char prevCh;
for (int i = 0; i < s.length(); i++) {
if (isNumeric(s[i])) { // update counter
counter = counter * 10 + (s[i] - '0');
if (isNumeric(s[i + 1]) == false || i + 1 == s.length()) {
// now, put previous character stacked
while (counter-- > 1) {
result.push_back(prevCh);
}
counter = 0;
}
}
else {
result.push_back(s[i]);
prevCh = s[i];
}
}
return result;
}
now, decode("a12bhr3") returns aaaaaaaaaaaabhrrr. it works well.

Convert string to float or integer without using built in functions (like atoi or atof)

I'm new to C++ and our teacher asked us to get a function that does the above title. So far I've got a function that converts a string to an integer, but I have no idea about how to modify it to make it work if the numbers in the string would represent a float.
int convert(char str[], int size) {
int number = 0;
for (int i = 0; i < size; ++i) {
number += (str[i] - 48)*pow(10, (size - i - 1));
}
return number;
}
If I run:
char myString[] = "12345";
convert(myString, 5);
I get:
12345
But if I run:
char myString[] = "123.45";
convert(myString, 5);
I get:
122845
How could I modify my program to work with floats too? I know convert function is meant to return an int so, should I use two more functions?
I was thinking about one that determinates if the string is inteded to be converted to an integer or a string, and the other that'll actually convert the string to a float.
Here is the function for doing so...
template<class T, class S>
T convert_string_to_number(S s)
{
auto result = T(0.l);
if (s.back() == L'F' || s.back() == L'f')
s = s.substr(0u, s.size() - 1u);
auto temp = s;
auto should_add = false;
if (!std::is_floating_point<T>::value)
{
should_add = temp.at(temp.find_first_of(L'.') + 1) >= '5';
temp.erase(temp.begin() + temp.find_first_of(L'.'), temp.end());
}
else if (temp.find_first_of(L'.') != S::npos)
temp.erase(temp.begin() + temp.find_first_of(L'.'));
for (int i = temp.size() - 1u; i >= 0; --i)
if (temp[i] >= L'0' && temp[i] <= L'9')
result += T(std::powl(10.l, temp.size() - i - 1.l) * (temp[i] - L'0'));
else
throw std::invalid_argument("Invalid numerical string!");
if (s.find(L'-') != S::npos)
result = -T(std::fabs(result));
if (s.find(L'.') != S::npos && std::is_floating_point<T>::value)
result /= T(std::powl(10.l, s.size() - s.find(L'.') - 1.l));
return std::is_floating_point<T>::value ? T(result) : T(result + T(should_add));
}
Just use it like you typically would...
auto some_number = convert_string_to_number<float>(myString);...
For the floating point part of the assignment: what about regular expressions? It is also kind of built-in functionality, but general purpose, not designed for your particular task, so I hope your teacher will be fine with this idea.
You can use the following regex: [+-]?([0-9]*[.])?[0-9]+ (I got it from this answer) to detect if provided string is a floating point number. Then you can modify the expression a little bit to capture the +/- signs and parts before/after the dot separator. Once you extract these features the task should be relatively simple.
Also please change your method signature to: float convert(const std::string& str).
Try this :
int convert(char str[], int size) {
int number = 0;
for (int i = 0; i < size; ++i) {
number += (str[i] - 48)*pow(10, (size - i - 1));
}
return number;
}
int pow10(int radix)
{
int r = 1;
for (int i = 0; i < radix; i++)
r *= 10;
return r;
}
float convert2float(char str[], int size) { //size =6
// convert to string_without_decimal
char str_without_decimal[10];
int c = 0;
for (int i = 0; i < size; i++)
{
if (str[i] >= 48 && str[i] <= 57) {
str_without_decimal[c] = str[i];
c++;
}
}
str_without_decimal[c] = '\0'; //str_without_decimal = "12345"
//adjust size if dot present or not. If no dot present => size = c
size = (size != c ?) size - 1 : size; //size = 5 = 6-1 since dot is present
//convert to decimal
int decimal = convert(str_without_decimal, size); //decimal = 12345
//get divisor
int i;
for (i = size; i >= 0; i--) {
if (str[i] == '.') break;
}
int divisor = pow10(size - i); //divisor = 10;
return (float)decimal/(float) divisor; // result = 12345 /10
}
int main()
{
char str[] = "1234.5";
float f = convert2float(str, 6);
cout << f << endl;
return 0;
}

How do you implement your own string to integer conversion

So I want to take a string in such as 8302 and convert this into an integer by creating my own function and not using the stoi/atoi functions.
I tried so far by doing:
int stringToInt(string input)
{
int i = 0;
while(input[i] >= '0' && input[i] <= '9')
{
input[i] = input[i] * 10 + ......
i++;
}
return i;
}
I know i need to keep multiplying by 10 everytime i find an integer so i can increase it e.g 123 = 1*10*10+2*10+3. but i dont know how to code this. could anyone suggest a method?
It might be easiest to do it in a recursive manner. Use the following idea:
8302 = 830 * 10 + 2
That is:
If there is only one char in the string - return it; otherwise continue
Separate the last char in the string
Convert the string (without the last char) to integer - using recursion
Multiply by 10 and add the last char
There are a lot of details here:
how to convert 1 char to an integer? - subtract '0' from it
how to separate a char from the rest of the string? - use substr
when you have a working recursive solution, you might want to convert it to an iterative solution - this will make it faster, but maybe less readable
what to do with invalid strings like "aaa" or "123haha" - my algorithm doesn't handle that
Before you could define a char2int conversion:
inline int ctoi(char c) {
switch (c) {
case '0':
return 0;
case '1':
return 1;
case '2':
return 2;
case '3':
return 3;
case '4':
return 4;
case '5':
return 5;
case '6':
return 6;
case '7':
return 7;
case '8':
return 8;
case '9':
return 9;
default:
throw std::runtime_error("Invalid char conversion");
}
}
And use it:
int my_stoi_dec(const std::string& str) {
int rtn = 0;
int exp = 1;
for (auto cp = str.crbegin(); cp != str.crend(); ++cp) {
char c = *cp;
if (isdigit(c)) {
rtn += ctoi(c) * exp;
exp *= 10;
} else if (c == '+') {
return rtn;
} else if (c == '-') {
return rtn * -1;
} else {
throw std::runtime_error("Integer error conversion");
}
}
}
This one is very close to your attempt:
int toInt(const std::string& input)
{
int i = 0;
for (const auto c : input)
{
if (c < '0' || c > '9')
break;
i = i*10 + c-'0';
}
return i;
}
The only assumption is that the characters '0' to '9' lie directly next to each other in the character set. The if statement makes sure that we stop at non-digit characters. The digit-characters are converted to their integer value using c-'0'.
Please keep in mind that this only parses the first digits of a string. Strings that start with a sign + or - are not considered.
A good way is to find your first digit and go from there make a multiplayer variable and multiply it by ten every digit. for every char you add you have to subtract '0' from it as '0' is not equal to int 0.
example:
string s = "12346";
int multiplayer = 1;
int i = 0;
int result = 0;
while (s[i] >= '0' && s[i] <= '9')
++i;
--i;
for(i ; i >= 0 ; --i){
result += (s[i] - '0') * multiplayer;
multiplayer *= 10;
}
It is much better that you start your conversion from right to left.
So you will iterate your string starting from its end, and ending on its beginning. On each iteration, we will take the character, convert it to int and multiply it with its multiplier (its position in the result integer) and then add it to the final result.
This should work:
#include <iostream>
#include <string>
int stringToInt(std::string input)
{
int result = 0;
int multiplier = 1;
for (int i = input.length() - 1; i >= 0; i--) // start from right
{
if (input[i] - '0' < 0 || input[i] - '0' > 9) // if any character is not an integer, return maximum negative
{
result = INT16_MIN;
break;
}
result += (input[i] - '0') * multiplier; // convert to int, get its position and then add it to result.
multiplier *= 10; // get next position
}
return result;
}
int main()
{
std::string MyEx = "123456";
int MyInt = stringToInt(MyEx);
std::cout << MyInt;
return 0;
}
void enforce( bool b ) {
if ( !b ) {
throw std::range_error("Not a valid string to convert to integer");
}
}
int stringToInt(std::string) {
for ( std::size_t i( 0 ); i != ( last - first ); ++i ) {
enforce( ( '0' <= first[ i ] ) && ( first[ i ] <= '9' ) );
result += pow(10,i) * ( first[ i ] - '0' );
}
}
Source

Read a float from a file on an SD card using Arduino/C++

I work with Arduino with Eclipse. I want to read a double from a configuration file. I have made these functions:
void Settings::readDouble(char buff, String data, bool flag, double dataToStore) {
data = "";
int intPart = 0;
int probablyFloatPart = 0;
while(buff != '\n') {
if(isdigit(buff)) {
data.concat(buff);
Serial.println(data);
}
else if (buff == '.') {
char charBuf[data.length()+1];
data.toCharArray(charBuf,data.length()+1);
// Convert chars to int
intPart = atoi(charBuf);
Serial.println(intPart);
data="";
flag = false;
}
buff = theSettings.read();
};
// Convert string to array of chars
char charBuf[data.length()+1];
data.toCharArray(charBuf,data.length()+1);
// Convert chars to integer
probablyFloatPart = atoi(charBuf);
Serial.println(probablyFloatPart);
if (!flag) {
dataToStore = intPart + (probablyFloatPart/pow(10, data.length()));
}
else {
dataToStore = probablyFloatPart;
}
flag = true;
//Serial.println(dataToStore, 5);
}
From serial.printing I noticed that if the number I want to read has more than four digits on either int or decimal part it gives an error. Basically, I can not tranform a string with more than four bytes to an int. How can I do this?
You will need to use long to store values over 32767 (or in an unsigned, over 65535).
I'll be back with a simpler piece of code - I think your code is more complex than it needs to be.
This code uses no standard functions except isdigit, and just walks through the number. No limitiation on the size of the number (obviosusly, if you go over the limit for a double, then it will fail). Error handling may need a bit of improvement. I have given it some mild testing...
double str_to_double(const char *data, size_t len)
{
double d = 0.0;
bool neg = false;
int index = 0;
if (data[index] == '-')
{
index++;
neg = true;
}
while(data[index] != '.' && index < len)
{
if (isdigit(data[index]))
{
d = d * 10 + (data[index] - '0');
index ++;
}
else
{
// Handle "bad input" ...
return -1;
}
}
if (data[index] == '.')
{
index ++;
double div = 10.0;
while(index < len)
{
if (isdigit(data[index]))
{
d += (data[index] - '0') / div;
div *= 10;
index++;
}
else
{
// Handle bad input
return -1;
}
}
}
// Flip sign if need be.
if (neg)
{
d = -d;
}
return d;
}
I would try to save the result in a float. The code below is what I would try.
float result = 0;
float scale = 10;
bool pastDecPoint = false;
while(buff != '\n') {
if(isdigit(buff){
if(pastDecPoint)
{
result = result + (buff / scale);
scale = scale * 10;
}
else
{
result = (result * 10.0) + atoi(buff);
}
} else if (buff == '.') {
pastDecPoint = true;
}
buff = theSettings.read();
};
Serial.println(result);
}
I also want to see what petersson comes back with. Which is very similar to Petersson

How should I approach a credit card number validation algorithm?

I'm writing a program to validate credit card numbers and I have to use Luhn's Algorithm. Let me say beforehand, that I have just started to learn to program (we covered loops like last week), so there a lot of things I am unfamiliar with. I am having trouble with one of my functions that checks the arithmetic. Basically, it has to double every second digit from right to left and add everything together. But if you double a number, like 5, and you get 10, then you will have to add 1+0=1 to the total sum instead of 10. That's the part I'm stuck on. How can I put that in a program?
Sample code so far:
int
doubleEvenSum(string creditCardNumber) {
int evenSum;
int countPosition;
int doublePosition;
int length;
length = creditCardNumber.length ();
countPosition = creditCardNumber.at(length - 2);
evenSum = 0;
while(countPosition>0) {
if ((2 * countPosition) < 10) {
doublePosition = 2 * countPosition;
}
else if ((2 * countPosition) > 9) {
???
}
evenSum = evenSum + doublePosition;
}
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/*
return the Luhn (MOD10) checksum for a sequence of digits.
-1 is returned on error (a non-digit was in the sequence
*/
int mod10( char const* s)
{
int len = strlen(s);
int sum = 0;
int dbl = 0;
while (len) {
char digit;
int val;
--len;
digit = s[len];
if (!isdigit( (unsigned char) digit)) return -1; // non digit in the sequence
val = digit - '0'; // convert character to numeric value
if (dbl) {
// double the value
val *= 2;
// if the result is double-digits, add the digits together
if (val > 9) {
val = val - 10;
val = val + 1;
}
}
dbl = !dbl; // only double value every other time
sum += val;
}
return sum % 10;
}
Here is a different algorithm. I cut/pasted from a C# example; the second link discusses a number of optimization for Luhn.
Please study this example, and please run it through the debugger to study how the code behaves as it's executing. Understanding how code actually runs (as opposed to how you think it will run when you write it) is an essential skill. IMHO....
/*
* Validate credit card with Luhn Algorithm
*
* REFERENCES:
* - http://jlcoady.net/c-sharp/credit-card-validation-in-c-sharp
* - http://orb-of-knowledge.blogspot.com/2009/08/extremely-fast-luhn-function-for-c.html
*/
#include <stdio.h> // printf(), scanf(), etc
#include <string.h> // strlen (), etc
#include <ctype.h> // isdigit(), etc
#if !defined(FALSE)
#define FALSE 0
#define TRUE ~FALSE
#endif
/*
* type definitions (should go in separate header)
*/
enum CardType {
MASTERCARD=1, BANKCARD=2, VISA=3, AMEX=4, DISCOVER=5, DINERS=6, JCB=7
};
/*
* function prototypes (should also go in header)
*/
int luhn (int number[], int len);
bool validate (CardType cardType, char *cardNumber);
/*
* program main
*/
int
main (int argc, char *argv[])
{
char cc_number[80];
int cc_type;
for ( ;; ) {
printf ("Enter a credit card number and type (1, 2, 3, 4, 5. 6 or 7):\n");
printf (" MASTERCARD=1, BANKCARD=2, VISA=3, AMEX=4, DISCOVER=5, DINERS=6, JCB=7\n");
int iret = scanf ("%s %d", cc_number, &cc_type);
if (iret == 2)
break;
else
printf ("Incorrect input: please enter a valid CC# and CC type\n");
}
if (validate ((CardType)cc_type, cc_number))
printf ("Valid\n");
else
printf ("Invalid card type/number\n");
return 0;
}
/*
* validate card#
*/
bool
validate (CardType cardType, char *cardNumber)
{
// 16 or fewer digits?
int len = strlen(cardNumber);
if (strlen (cardNumber) > 16)
return false;
// number to validate
int number[16];
for(int i = 0; i < (int)strlen (cardNumber); i++) {
if(!isdigit(cardNumber[i]))
return FALSE;
number[i] = cardNumber[i] - '0';
}
// Validate based on card type, first if tests length, second tests prefix
switch(cardType) {
case MASTERCARD:
if(len != 16)
return FALSE;
if(number[0] != 5 || number[1] == 0 || number[1] > 5)
return FALSE;
break;
case BANKCARD:
if(len != 16)
return FALSE;
if(number[0] != 5 || number[1] != 6 || number[2] > 1)
return FALSE;
break;
case VISA:
if(len != 16 && len != 13)
return FALSE;
if(number[0] != 4)
return FALSE;
break;
case AMEX:
if(len != 15)
return FALSE;
if(number[0] != 3 || (number[1] != 4 && number[1] != 7))
return FALSE;
break;
case DISCOVER:
if(len != 16)
return FALSE;
if(number[0] != 6 || number[1] != 0 || number[2] != 1 || number[3] != 1)
return FALSE;
break;
case DINERS:
if(len != 14)
return FALSE;
if(number[0] != 3 || (number[1] != 0 && number[1] != 6 && number[1] != 8) || number[1] == 0 && number[2] > 5)
return FALSE;
break;
case JCB:
if(len != 16 && len != 15)
return FALSE;
if(number[0] != 3 || number[1] != 5)
return FALSE;
break;
default:
return FALSE;
}
int sum = luhn (number, len);
return (sum % 10 == 0);
}
// Use Luhn Algorithm to validate
int luhn (int number[], int len)
{
int sum = 0;
for(int i = len - 1; i >= 0; i--)
{
if(i % 2 == len % 2)
{
int n = number[i] * 2;
sum += (n / 10) + (n % 10);
}
else
sum += number[i];
}
return sum;
}
int luhnCardValidator(char cardNumbers[]) {
int sum = 0, nxtDigit, i;
for (i = 0; cardNumbers[i] != NULL_TERMINATOR ; i++) {
nxtDigit = cardNumbers[i] - START_OF_ASCII_NUMERIC;
if (i % 2 == 0)
nxtDigit = (nxtDigit > 4) ? (nxtDigit * 2 - 10) + 1 : nxtDigit * 2;
sum += nxtDigit;
}
return (sum % 10);
}
This:
... (nxtDigit > 4) ? (nxtDigit * 2 - 10) + 1 : ...
is the clever bit. If the digit is greater than 4, then the doubling will be 10 or more. In that case, you take the doubled number and subtract 10 which will give you the ones-digit then you add 1 (the tens-digit).
Just subtract 9 from the double of the number then you will equivalent of the sum of the digits.
For ex.
7= 7*2 = 14 = 1+4 = 5 OR 14-9 = 5
This is more efficient than writing code for adding both digits.