Strange response from program - c

Okay i have written up a code that converts a string to a decimal
however i am not understanding why it is core dumping on the second assert when the first one works fine
if i print out the return from the function it is actually returning what it is supposed to be returning but for some reason the assert fails
so can somebody help me understand what is going on with assert here? i dont understand why it is failing?
here is the code
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <assert.h>
double myAtoD(char *);
int main(int argc, char *argv[]){
char *k="0.1";
char *l="0.141";
double m,n;
m = myAtoD(k);
n = myAtoD(l);
printf("%f\n",m);
assert(m==0.1);
printf("%f\n",n);
assert(n==0.141);
return 0;
}
double myAtoD (char *message){
int i=0;
int prior=0;
double num=0;
while(message[i]!='.'){
prior++;
i++;
}
i=0;
prior--;
while(message[i]!='\0'){
if(message[i]=='.')
i++;
printf("num is %f\nnew number to be added is %f\n\n",num, (message[i] - '0')*pow(10,prior));
num += (message[i] - '0')*pow(10,prior);
i++;
prior--;
}
return num;
}

You dealing with double precision issues.
The number 0.141 cannot be represented exactly by the computer, so you will have to instead check whether it is a small epsilon away from 0.141 instead.
Here is a better assertion.
assert(abs(n-0.141) < DBL_EPSILON);
For that constant, you will need to put #include <float.h> at the top of your source file.

A candidate improvement to myAtoD() that is more computationally stable. Although it provides a nice answer for "0.141", it may nor be the best for all input - need to do more testing. But #merlin2011 suggestion to allow a little wiggle room is good.
#include <ctype.h>
#include <math.h>
double myAtoD (const char *message){
double num = 0.0;
int dp = '.';
size_t dpi;
size_t i = 0;
for (i=0; message[i]; i++) {
int ch = (unsigned char) message[i];
if (ch == dp) {
dp = '\0';
dpi = i;
continue;
}
if (isdigit(ch)) {
num = num*10 + (ch - '0');
}
else {
break; // illegal character
}
}
if (dp == '\0') {
num /= pow10(i - dpi - 1);
}
return num;
}
[Edit]
A very precise and robust conversion of text to a double is non-trivial - best to do with some sort of extended precision. The above simple routine is certain to provide an answer within 2 ULP.

Related

How to convert a string to double floating-point value?

sorry if the title is confusing, I just need some experienced programmers to attempt to clarify something for me. So one of my homework questions asks me this (the language is c):
Write a function that receives a string and returns a double floating-point value. The fucntion declaration would look like this
double convertFloat(char s[]);
Do not use the strtol() function or any other standard c library function. Write your own!... Note that the input string could have any of the following sample formats: "1.34", "-1.4554", "6".
I am just not sure how to approach this because I am not sure what is being asked here. If I had to write a function to find a square root or something like that I could do it no problem but it seems that I have to take strings that are all numbers and convert them to floats... just not sure where to start and googling similar things has yielded no results.
Thank you all in advance.
- Davey
Start by writing
unsigned int convertUnsignedInt(const char *s);
// e.g. convertUnsignedInt("42") == 42
It's a bit simpler because you don't have to handle negative numbers or fractions, but it shows the general principle.
Here is the complete code to your problem. The code is understandable. Ask if you have any doubts regarding it.
#include <stdio.h>
int power(int n, int m)
{
if (m == 1)
return n;
else
return n * (power(n, m - 1));
}
int getLength(char s[])
{
int i = 0;
for(i = 0; s[i] != '\0'; i++);
return i;
}
double convertFloat(char s[])
{
int len = getLength(s);
int dotpos = 0;
double result = 0.0f;
int n = 0, flag = 0;
if(s[0] == '-')
{
n = 1;
flag = 1;
}
for (; n < len; n++)
{
if (s[n] == '.')
{
dotpos = len - n - 1;
}
else
{
result = result * 10 + (s[n] - '0');
}
}
result /= power(10, dotpos);
if(flag)
return result*-1;
return result;
}
int main()
{
char str[] = "126433.47";
printf("%f", convertFloat(str));
}

Reverse a character array without changing value of number?

I have for example a string (mathematical equation in postfix notation) that looks like this: The numbers are 5.33,5.32,6.33,3.22
5.335.32*6.333.22++
I'm looking to make it into prefix notation but simply reversing the string won't work due to the fact it has to retain the value of the number.
I've thought of doing a normal character by character swap in a for loop, and when encountering a digit make that into a substring and place it on afterwards but I haven't gotten it to work properly and now I'm stuck.
My end-goal is to make a binary expression tree out of that, so if there's an easier way than doing this also please let me know.
A stack-based approach:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *postfix_to_prefix(const char *string) {
char operator, *stack[1024];
int s = 0, number, fraction;
const char *tokens = string;
while (1) {
if (sscanf(tokens, "%1d.%2d", &number, &fraction) == 2) {
stack[s] = malloc(sizeof("1.00"));
(void) sprintf(stack[s++], "%4.2f", number + (fraction / 100.0));
tokens += strlen("1.00");
} else if (sscanf(tokens, "%c", &operator) == 1) {
char *operand1 = stack[--s];
char *operand2 = stack[--s];
stack[s] = malloc(strlen(operand1) + strlen(operand1) + sizeof(operator) + sizeof('\0'));
(void) sprintf(stack[s++], "%c%s%s", operator, operand1, operand2);
free(operand1);
free(operand2);
tokens += sizeof(operator);
} else {
break;
}
}
return stack[--s];
}
int main() {
const char *string = "5.335.32*6.333.22++";
printf("%s\n", string);
char *inverted = postfix_to_prefix(string);
printf("%s\n", inverted);
free(inverted);
return 0;
}
OUTPUT
> ./a.out
5.335.32*6.333.22++
++3.226.33*5.325.33
>
This is a bare bones implementation with no real error checking nor other finishing touches. You'll want to check that non-communitive operations like subtraction and division come out with the operands in the correct order and reverse them if not.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(void) {
char exp[] = "5.335.32*6.333.22++";
size_t len = strlen(exp);
char temp[len];
char *p = temp;
for(int i = len-1; i >= 0; ){
if(isdigit(exp[i])){
memcpy(p, &exp[i-4+1], 4);//all number have a length of 4
p += 4;
i -= 4;
} else {
*p++ = exp[i--];//length of op is 1
}
}
memcpy(exp, temp, len);//Write back
puts(exp);//++3.226.33*5.325.33
return 0;
}

Islower function glitch

I'm doing the CS50x class and I am stuck at a glitch. I asked them what was going on and no one knew what was going on.
Whenever I try to print a lowercase f it always comes up as ?. Try doing 23 as the argument and abcdefghijklmnopqrstuvwxyz as the input. It's messed up. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
int main (int argc, string argv[]){
if(argc !=2){
return 1;
}
string x = GetString();
int key = atoi(argv[1]);
for(int a = 0, n = strlen(x); a < n; a++){
char i = key + x[a];
if(islower(x[a])){
if(i > 122){
i = (i-122) + 96;
}
}
if(isupper(x[a])){
if(i > 90){
i = (i-90) + 64;
}
}
printf("%c", i);
}
printf("\n");
return 0;
}
I suspect it's because your char i defaults to signed. When you add 23 to a lowercase letter, anything that is above 104 (being 127-23) is going to wrap around into negatives. Looking at your code, it will stay negative because it fails the subsequent tests and does not get modified.
It's usually best to do char arithmetic with int, then convert back to char... But you could probably fix this by using unsigned char.

Grab all integers from irregular strings in C

I am looking for a (relatively) simple way to parse a random string and extract all of the integers from it and put them into an Array - this differs from some of the other questions which are similar because my strings have no standard format.
Example:
pt112parah salin10n m5:isstupid::42$%&%^*%7first3
I would need to eventually get an array with these contents:
112 10 5 42 7 3
And I would like a method more efficient then going character by character through a string.
Thanks for your help
A quick solution. I'm assuming that there are no numbers that exceed the range of long, and that there are no minus signs to worry about. If those are problems, then you need to do a lot more work analyzing the results of strtol() and you need to detect '-' followed by a digit.
The code does loop over all characters; I don't think you can avoid that. But it does use strtol() to process each sequence of digits (once the first digit is found), and resumes where strtol() left off (and strtol() is kind enough to tell us exactly where it stopped its conversion).
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
int main(void)
{
const char data[] = "pt112parah salin10n m5:isstupid::42$%&%^*%7first3";
long results[100];
int nresult = 0;
const char *s = data;
char c;
while ((c = *s++) != '\0')
{
if (isdigit(c))
{
char *end;
results[nresult++] = strtol(s-1, &end, 10);
s = end;
}
}
for (int i = 0; i < nresult; i++)
printf("%d: %ld\n", i, results[i]);
return 0;
}
Output:
0: 112
1: 10
2: 5
3: 42
4: 7
5: 3
More efficient than going through character by character?
Not possible, because you must look at every character to know that it is not an integer.
Now, given that you have to go though the string character by character, I would recommend simply casting each character as an int and checking that:
//string tmp = ""; declared outside of loop.
//pseudocode for inner loop:
int intVal = (int)c;
if(intVal >=48 && intVal <= 57){ //0-9 are 48-57 when char casted to int.
tmp += c;
}
else if(tmp.length > 0){
array[?] = (int)tmp; // ? is where to add the int to the array.
tmp = "";
}
array will contain your solution.
Just because I've been writing Python all day and I want a break. Declaring an array will be tricky. Either you have to run it twice to work out how many numbers you have (and then allocate the array) or just use the numbers one by one as in this example.
NB the ASCII characters for '0' to '9' are 48 to 57 (i.e. consecutive).
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
int main(int argc, char **argv)
{
char *input = "pt112par0ah salin10n m5:isstupid::42$%&%^*%7first3";
int length = strlen(input);
int value = 0;
int i;
bool gotnumber = false;
for (i = 0; i < length; i++)
{
if (input[i] >= '0' && input[i] <= '9')
{
gotnumber = true;
value = value * 10; // shift up a column
value += input[i] - '0'; // casting the char to an int
}
else if (gotnumber) // we hit this the first time we encounter a non-number after we've had numbers
{
printf("Value: %d \n", value);
value = 0;
gotnumber = false;
}
}
return 0;
}
EDIT: the previous verison didn't deal with 0
Another solution is to use the strtok function
/* strtok example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "pt112parah salin10n m5:isstupid::42$%&%^*%7first3";
char * pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str," abcdefghijklmnopqrstuvwxyz:$%&^*");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " abcdefghijklmnopqrstuvwxyz:$%&^*");
}
return 0;
}
Gives:
112
10
5
42
7
3
Perhaps not the best solution for this task, since you need to specify all characters that will be treated as a token. But it is an alternative to the other solutions.
And if you don't mind using C++ instead of C (usually there isn't a good reason why not), then you can reduce your solution to just two lines of code (using AXE parser generator):
vector<int> numbers;
auto number_rule = *(*(axe::r_any() - axe::r_num())
& *axe::r_num() >> axe::e_push_back(numbers));
now test it:
std::string str = "pt112parah salin10n m5:isstupid::42$%&%^*%7first3";
number_rule(str.begin(), str.end());
std::for_each(numbers.begin(), numbers.end(), [](int i) { std::cout << "\ni=" << i; });
and sure enough, you got your numbers back.
And as a bonus, you don't need to change anything when parsing unicode wide strings:
std::wstring str = L"pt112parah salin10n m5:isstupid::42$%&%^*%7first3";
number_rule(str.begin(), str.end());
std::for_each(numbers.begin(), numbers.end(), [](int i) { std::cout << "\ni=" << i; });
and sure enough, you got the same numbers back.
#include <stdio.h>
#include <string.h>
#include <math.h>
int main(void)
{
char *input = "pt112par0ah salin10n m5:isstupid::42$%&%^*%7first3";
char *pos = input;
int integers[strlen(input) / 2]; // The maximum possible number of integers is half the length of the string, due to the smallest number of digits possible per integer being 1 and the smallest number of characters between two different integers also being 1
unsigned int numInts= 0;
while ((pos = strpbrk(pos, "0123456789")) != NULL) // strpbrk() prototype in string.h
{
sscanf(pos, "%u", &(integers[numInts]));
if (integers[numInts] == 0)
pos++;
else
pos += (int) log10(integers[numInts]) + 1; // requires math.h
numInts++;
}
for (int i = 0; i < numInts; i++)
printf("%d ", integers[i]);
return 0;
}
Finding the integers is accomplished via repeated calls to strpbrk() on the offset pointer, with the pointer being offset again by an amount equaling the number of digits in the integer, calculated by finding the base-10 logarithm of the integer and adding 1 (with a special case for when the integer is 0). No need to use abs() on the integer when calculating the logarithm, as you stated the integers will be non-negative. If you wanted to be more space-efficient, you could use unsigned char integers[] rather than int integers[], as you stated the integers will all be <256, but that isn't a necessity.

Is there a strtol equivalent that does not require a null-terminated string?

Is there a standard C function similar to strtol which will take a char* and a length for a non-null-terminated string?
I know that I could copy out the string into a null-terminated region, but for efficiency reasons that is undesirable.
No such function in the standard library. You will either have to use the temporary buffer method, or write your own function from scratch.
To answer your question: no, there is no standard function, but it is simple enough to write your own:
#include <stdio.h>
#include <ctype.h>
int natoi(char *s, int n)
{
int x = 0;
while(isdigit(s[0]) && n--)
{
x = x * 10 + (s[0] - '0');
s++;
}
return x;
}
int main(int argc, char*argv[])
{
int i;
for(i = 1; i < argc; i++)
printf("%d: %d\n", i, natoi(argv[i], 5));
}
strntol is probably what you're after... it's not standard C, though.
If you're that pressed for efficiency, you can probably motivate the time to write and debug your own.
But: just do it with a copy; you probably have an upper bound for how long the string can be (a decimal numeral that fits in a long has a strict upper bound on its maximum length), so you can have a static buffer. Then profile your entire application, and see if the copying/conversion really is a bottleneck. If it really is, then you know you need to write your own.
Here's a rough (untested, browser-written) starting point:
long limited_strtol(const char *string, size_t len)
{
long sign = 1;
long value = 0;
for(; len > 0 && *string == '-'; string++, len--)
sign *= -1;
for(; len > 0 && isdigit(*string); string++, len--)
{
value *= 10;
value += *string - '0';
len--;
string++;
}
return sign * value;
}

Resources