I'm studying K&R book. I'm currently at chapter 4. I was reading the atof() function on page 71. Function atof(s) converts string to its double precision floating point equivalent.
The code of atof() is as following:
//atof: convert string s to double
double atof2(char s[])
{
double val, power;
int i, sign;
for (i = 0; isspace(s[i]); ++i) //skip white space
;
sign = (s[i] == '-') ? -1: 1;
if (s[i] == '-' || s[i] == '-')
++i;
for (val = 0.0; isdigit(s[i]); i++)
val = 10.0 * val + (s[i] - '0');
if (s[i] == '.')
++i;
for (power = 1.0; isdigit(s[i]); i++) {
val = 10.0 * val + (s[i] - '0');
power *= 10.0;
}
return sign * val / power;
}
My question is about variable: power. Why do we need it for?
I do understand the use of variable: "val" but i'm not sure about variable: "power". Why do we divide val by power?
Variable power is for division of number by power , to get result as float point .
Let your string be -12.83 , then first for loop will check for space and increment i as no space so ,i=0 .
sign will be -1 as s[i]=s[0]='-' .
In next two loops string's values are converted to integers and stored in val ( excluding . - figure out yourself) .
Now after both loop val will be 1283 . But last loop will iterate for 2 times and power will be changed to 100.00 (10*1.0 in first iteration and 10*10.0 in second iteration) .
Now to get value as float point val is divided by power and multiplied by sign .
So , what it will return is -1*1283/100 , thus -12.83 is your float point number .
Related
I am trying to code a program that will take a floating point number in base 10 and convert its fractional part in base 2. In the following code, I am intending to call my converting function into a printf, and format the output; the issue I have lies in my fra_binary() where I can't figure out the best way to return an integer made of the result of the conversion at each turn respectively (concatenation). Here is what I have done now (the code is not optimized because I am still working on it) :
#include <stdio.h>
#include <math.h>
int fra_binary(double fract) ;
int main()
{
long double n ;
double fract, deci ;
printf("base 10 :\n") ;
scanf("%Lf", &n) ;
fract = modf(n, &deci) ;
int d = deci ;
printf("base 2: %d.%d\n", d, fra_binary(fract)) ;
return(0) ;
}
int fra_binary(double F)
{
double fl ;
double decimal ;
int array[30] ;
for (int i = 0 ; i < 30 ; i++) {
fl = F * 2 ;
F = modf(fl, &decimal) ;
array[i] = decimal ;
if (F == 0) break ;
}
return array[0] ;
}
Obviously this returns partly the desired output, because I would need the whole array concatenated as one int or char to display the series of 1 and 0s I need. So at each turn, I want to use the decimal part of the number I work on as the binary number to concatenate (1 + 0 = 10 and not 1). How would I go about it?
Hope this makes sense!
return array[0] ; is only the first value of int array[30] set in fra_binary(). Code discards all but the first calculation of the loop for (int i = 0 ; i < 30 ; i++).
convert its fractional part in base 2
OP's loop idea is a good starting point. Yet int array[30] is insufficient to encode the fractional portion of all double into a "binary".
can't figure out the best way to return an integer
Returning an int will be insufficient. Instead consider using a string - or manage an integer array in a likewise fashion.
Use defines from <float.h> to drive the buffer requirements.
#include <stdio.h>
#include <math.h>
#include <float.h>
char *fra_binary(char *dest, double x) {
_Static_assert(FLT_RADIX == 2, "Unexpected FP base");
double deci;
double fract = modf(x, &deci);
fract = fabs(fract);
char *s = dest;
do {
double d;
fract = modf(fract * 2.0, &d);
*s++ = "01"[(int) d];
} while (fract);
*s = '\0';
// For debug
printf("%*.*g --> %.0f and .", DBL_DECIMAL_DIG + 8, DBL_DECIMAL_DIG, x,
deci);
return dest;
}
int main(void) {
// Perhaps 53 - -1021 + 1
char fraction_string[DBL_MANT_DIG - DBL_MIN_EXP + 1];
puts(fra_binary(fraction_string, -0.0));
puts(fra_binary(fraction_string, 1.0));
puts(fra_binary(fraction_string, asin(-1))); // machine pi
puts(fra_binary(fraction_string, -0.1));
puts(fra_binary(fraction_string, DBL_MAX));
puts(fra_binary(fraction_string, DBL_MIN));
puts(fra_binary(fraction_string, DBL_TRUE_MIN));
}
Output
-0 --> -0 and .0
1 --> 1 and .0
3.1415926535897931 --> 3 and .001001000011111101101010100010001000010110100011
-0.10000000000000001 --> -0 and .0001100110011001100110011001100110011001100110011001101
1.7976931348623157e+308 --> 179769313486231570814527423731704356798070600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 and .0
2.2250738585072014e-308 --> 0 and .00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
4.9406564584124654e-324 --> 0 and .000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
Also unclear why input is long double, yet processing is with double. Recommend using just one FP type.
Note that your algorithm finds out the binary representation of the fraction most significant bit first.
One way to convert the fractional part to a binary string, would be to supply the function with a string and a string length, and have the function fill it with up to that many binary digits:
/* This function returns the number of chars needed in dst
to describe the fractional part of value in binary,
not including the trailing NUL ('\0').
Returns zero in case of an error (non-finite value).
*/
size_t fractional_bits(char *dst, size_t len, double value)
{
double fraction, integral;
size_t i = 0;
if (!isfinite(value))
return 0;
if (value > 0.0)
fraction = modf(value, &integral);
else
if (value < 0.0)
fraction = modf(-value, &integral);
else {
/* Zero fraction. */
if (len > 1) {
dst[0] = '0';
dst[1] = '\0';
} else
if (len > 0)
dst[0] = '\0';
/* One binary digit was needed for exact representation. */
return 1;
}
while (fraction > 0.0) {
fraction = fraction * 2.0;
if (fraction >= 1.0) {
fraction = fraction - 1.0;
if (i < len)
dst[i] = '1';
} else
if (i < len)
dst[i] = '0';
i++;
}
if (i < len)
dst[i] = '\0';
else
if (len > 0)
dst[len - 1] = '\0';
return i;
}
The above function works very much like snprintf(), except it takes only the double whose fractional bits are to be stored as a string of binary digits (0 or 1). and returns 0 in case of an error (non-finite double value).
Another option is to use an unsigned integer type to hold the bits. For example, if your code is intended to work on architectures where double is an IEEE-754 Binary64 type or similar, the mantissa has up to 53 bits of precision, and an uint64_t would suffice.
Here is an example of that:
uint64_t fractional_bits(const double val, size_t bits)
{
double fraction, integral;
uint64_t result = 0;
if (bits < 1 || bits > 64) {
errno = EINVAL;
return 0;
}
if (!isfinite(val)) {
errno = EDOM;
return 0;
}
if (val > 0.0)
fraction = modf(val, &integral);
else
if (val < 0.0)
fraction = modf(-val, &integral);
else {
errno = 0;
return 0;
}
while (bits-->0) {
result = result << 1;
fraction = fraction * 2.0;
if (fraction >= 1.0) {
fraction = fraction - 1.0;
result = result + 1;
}
}
errno = 0;
return result;
}
The return value is the binary representation of the fractional part: [i]fractional_part[/i] ≈ [i]result[/i] / 2[sup][i]bits[/i][/sup], where [i]bits[/i] is between 1 and 64, inclusive.
In order for the caller to detect an error, the function clears errno to zero if no error occurred. If an error does occur, the function returns zero with errno set to EDOM if the value is not finite, or to EINVAL if bits is less than 1 or greater than 64.
You can combine the two approaches, if you implement an arbitrary-size unsigned integer type, or a bitmap type.
I understand what we are doing, before we converted a string to an int, now we are converting a string to a double. I don't understand the logic behind this code though. Could someone clarify this a little for me? Best regards.
#include <ctype.h>
#include <stdio.h>
//atof: convert string s to double
double atof(char s[])
{
double val, power;
int i, sign;
for (i = 0; isspace(s[i]); i++) //skip whitespace
;
sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-')
i++;
for (val = 0.0; isdigit(s[i]); i++)
val = 10.0 * val + (s[i] - '0');
if (s[i] == '.')
i++;
for (power = 1.0; isdigit(s[i]); i++) {
val = 10.0 * val + (s[i] - '0');
power *= 10.0;
}
return sign * val / power;
}
int main()
{
char s[] = "78.93"; //output is 78.930000
printf("atof equals %f\n", atof(s));
return 0;
}
This part is pretty easy, just skips to the first non-whitespace character:
for (i = 0; isspace(s[i]); i++) //skip whitespace
;
Now we check if the first non-whitespace character is a - to set it negative, then skip over the character whether its a - or a +:
sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-')
i++;
Now it starts to get tricky. Let's use an example of 1234.5678. First we're going to handle the part before the decimal. Its handled by looking at each digit, adding it to val, then if the next digit is not a decimal, multiply val up to the point by 10 to left shift it and add the next digit. For example with 1234.5678, we first see digit 1, add it to val for a val of 1. The next digit is 2, so we multiple current val (1) by 10 to get 10 then add 2 to get 12. The next digit is 3, so we multiply the current val (12) by 10 to get 120, then add 3 to get 123. The next digit is 4, so we multiple the current val (123) by 10 to get 1230, then add 4 to get 1234. Then the '.' is not a digit, so we've finished the left side of the number.
for (val = 0.0; isdigit(s[i]); i++)
val = 10.0 * val + (s[i] - '0');
This part just moves past the dot.
if (s[i] == '.')
i++;
Now we do the same with the right side of the decimal as we did with the left, but we also track how many digits are past the decimal (with the power variable). In the example of 1234.5678, the first digit we see is 5. So we multiply the current val (1234) by 10 and add 5 for (12345). We also increase our power to 10.0. This continues until we get a val of 123456789 and a power of 10000.0.
for (power = 1.0; isdigit(s[i]); i++) {
val = 10.0 * val + (s[i] - '0');
power *= 10.0;
}
Finally, we divide by the power to get the decimal place in the correct spot (123456789 / 10000.0):
return sign * val / power;
double atof(char s[])
{
double val, power;
int i, sign;
// if there is any leading 'white space', step index past it
// keep stepping index until other than white space encountered
for (i = 0; isspace(s[i]); i++)
;
// if there is a '-' char
// then indicate value is negative
// else assume value is positive
// format is: result = (condition)? true value : false value
sign = (s[i] == '-') ? -1 : 1;
// if there is a sign byte, step index past it
if (s[i] == '+' || s[i] == '-')
i++;
// initialize the result 'val'
// then loop through following characters
for (val = 0.0; isdigit(s[i]); i++)
// digits are in the range 0x30 through 0x39
// make them integers by subtracting 0x30 ('0')
// and update the result 'val'
// remembering that each successive digit pushes the current result 'val'
// to 10 times the old value then add the new 'converted' digit
val = 10.0 * val + (s[i] - '0');
// this ends the 'for' code block
// when execution gets here, encountered something other than a digit
// when a '.' encountered, step the index past it
if (s[i] == '.')
i++;
// the 'power' value is indicating how much to divide the resulting
// 'val' by to place the decimal point (if there was a decimal point)
// into the correct position
// if other than a digit encountered, exit loop
for (power = 1.0; isdigit(s[i]); i++)
{
val = 10.0 * val + (s[i] - '0'); // see above comment about a similar line of code
power *= 10.0;
} // end for
// calculate the actual value by allowing for any sign (+ or -)
// then dividing that result by 'power' to properly place the decimal point
return sign * val / power;
} // end function: atof
Skip the white space; handle a leading sign; compute the integer part (in Val); skip the decimal; handle the fractional part (by updating Val as if there were no decimal point, but also power to account for it).
This code consists of 3 loops
the first loop keep reading 'spaces' until something readable has been detected (a sign or a number)
the second loop calculate the value of the left part of the floating point (the value of xxx in -xxx.545)
the last loop uses the value of the previous loop and continue with the right part of the 'point'
while calculating the number 'power' which is the 10 to the power of number of elements after the '.'
now that we have a sign and value of both left and right parts of the floating point number
now in simple example: let -12.345
sign = -1
val = 12345
power = 1000 ( 10 to the power of numbers after the '.')
result is -1 * 12345 / 1000 = -12.345
So I'm hoping to get a little guidance on this one. I have a function that takes a radix(base) and then using getchar() will get the number to convert from the given radix to an integer representation.
The only argument given is the radix number, then getchar() gets the number representation via the command line.
So if I pass
str2int 16
input a number: 3c
It should output (16^1*3) + (16^0*12) = 48 + 12 = 60.
I fully understand the math, and different ways of converting bases, but don't know how to go about coding something up. The math is always MUCH easier than the code, at least to me.
Another way to compute would be:
(702) base 15 = 15*7 + 0 = 105; 15*105 + 2 = 1577
I don't know how to express this in C only using getchar()? Is it possible to do without using the math function?
Keep getting one char at a time until not a digit or no more are needed.
unsigned shparkison(unsigned base) {
unsigned sum = 0;
int ch;
while ((ch = getchar()) != EOF) {
// one could instead look up the toupper(value) in an array "0123...ABC...Z";
// Following assumes ASCII
if (isdigit(ch)) ch -= '0';
else if (islower(ch)) ch -= 'A' - 10;
else if (isupper(ch)) ch -= 'a' - 10;
else {
break; // Not a digit
}
if (ch >= base) {
break; // Digit too high
}
unsigned sum_old = sum;
sum *= base;
sum += ch;
if (sum < sum_old) {
sum = sum_old;
break; // Overflow
}
}
ungetc(ch, stdin);
return sum;
}
I have written a program in C to convert a floating point number represented in binary (1101.11) into a decimal (13.75).
However, I cannot seem to get the correct value out of the algorithm.
What is the correct method for converting a binary floating point number into a decimal?
I am using Dev CPP compiler (32 bit). The algorithm is defined below:
void b2d(double p, double q )
{
double rem, dec=0, main, f, i, t=0;
/* integer part operation */
while ( p >= 1 )
{
rem = (int)fmod(p, 10);
p = (int)(p / 10);
dec = dec + rem * pow(2, t);
t++;
}
/* fractional part operation */
t = 1; //assigning '1' to use 't' in new operation
while( q > 0 )
{
main = q * 10;
q = modf(main, &i); //extration of frational part(q) and integer part(i)
dec = dec+i*pow(2, -t);
t++;
}
printf("\nthe decimal value=%lf\n",dec); //prints the final output
}
int main()
{
double bin, a, f;
printf("Enter binary number to convert:\n");
scanf("%lf",&bin);
/* separation of integer part and decimal part */
a = (int)bin;
f = bin - a;
b2d(a, f); // function calling for conversion
getch();
return 0;
}
You are not, as you believe, reading "1101.11" as a floating point number represented in binary. You are reading it as a base-10 floating point number converted into an IEEE double-precision floating-point value, and then trying to change the base.
The inherent imprecision of this intermediate step is the reason for your problem.
A better approach, as suggested by Vicky, is to:
read "1101.11" as a string or line of text
convert the whole and fractional parts (whole=b1101=13 and numerator=b11=3, denominator=4)
re-combine these into whole + numerator/denominator = 13.75
Solution
The following will work as expected:
Output:
➤ gcc bin2dec.c -lm -o bin2dec && bin2dec
1101.11 -> 13.750000
1101 -> 13.000000
1101. -> 13.000000
.11 -> 0.750000
Code (bin2dec.c):
#include <stdio.h>
#include <math.h>
double convert(const char binary[]){
int bi,i;
int len = 0;
int dot = -1;
double result = 0;
for(bi = 0; binary[bi] != '\0'; bi++){
if(binary[bi] == '.'){
dot = bi;
}
len++;
}
if(dot == -1)
dot=len;
for(i = dot; i >= 0 ; i--){
if (binary[i] == '1'){
result += (double) pow(2,(dot-i-1));
}
}
for(i=dot; binary[i] != '\0'; i++){
if (binary[i] == '1'){
result += 1.0/(double) pow(2.0,(double)(i-dot));
}
}
return result;
}
int main()
{
char bin[] = "1101.11";
char bin1[] = "1101";
char bin2[] = "1101.";
char bin3[] = ".11";
printf("%s -> %f\n",bin, convert(bin));
printf("%s -> %f\n",bin1, convert(bin1));
printf("%s -> %f\n",bin2, convert(bin2));
printf("%s -> %f\n",bin3, convert(bin3));
return 0;
}
Explanation
The above code works by first finding the index of the decimal point in the number.
Once that is known, it walks the string both backwards and forwards from this index, adding the appropriate value to the result variable.
The first loop walks backwards from the decimal point and accumulates the powers of 2 if the character is 1. It takes the distance from the decimal point as the power of two, minus one for the indexing to be correct. Ie, it accumulates :
pow(2,<distance-from-decimal-point>)
The loop stops when the index reaches the beginning of the string.
The second loop walks forward until the end of the string, and deals with the fractional part as expected it also uses the distance from the index, but this time accumulates fractional parts:
1/pow(2,<distance-from-decimal-point>)
Worked out example:
1101.11 = 1101 + 0.11
1101 = 1*2^3 + 1*2^2 + 0*2^1 + 1*2^0 = 8 + 4 + 0 + 1 = 13
0.11 = 1/(2^1) + 1/(2^2) = 0.5 + 0.25 = 0.75
1101.11 = 13.75
Beware of malformed input. "10gsh.9701072.67812" will give you a result. It won't mean much :)
This piece of code behaves abnormally: I added some simple print statement
while(q>0)
{
double i;
main=q*10.0;
q=modf(main, &i); //extration of frational part(q) and integer part(i)
cout << "main = " << main << " frac part " << q << " int part " << i << endl;
cin.get();
dec=dec+i*pow(2,-t);
t++;
}
When you input 1101.11, the following output shown:
Enter binary number to convert(e.g: 1101.11 which will be 13.75 in decimal):
1101.11
bin in main 1101.11
p 1101 q 0.11
//inside the above while loop code
main = 1.1 frac part 0.1 int part 1
main = 1 frac part 1 int part 0 //^^^^^Error, given main=1, it should output integer part 1, fraction part 0
main = 10 frac part 1 int part 9 //^^^^^same strange error here, it should exit while already
So you got wrong result. I tested modf separately with input 1, it gave correct result.
So my guess is that you are reading the binary number as double, then tries to convert this double to binary back. There might be something going on under the hood for the precision of number though it shows that it is 1101.11. As suggested by #Useless, You may need to read the number as a string, figure out the substring before and after the decimal point . Then convert this two part into decimal separately.
#define MAXBUF 1000
int buf[MAXBUF];
int buffered = 0;
int bufp = 0;
int getch()
{
if(bufp > 0) {
if(!--bufp)
buffered = 0;
return buf[bufp];
}
else {
buffered = 0;
return getchar();
}
}
void ungetch(int c)
{
buf[bufp++] = c;
buffered = 1;
}
int getfloat(float *pn)
{
int c, sign, sawsign;
float power = 1.0;
while(isspace(c=getch()))
;
if(!isdigit(c) && c!= '+' && c!= '-' && c != '.') {
ungetch(c);
return 0;
}
sign = (c == '-') ? -1 : 1;
if(sawsign = (c == '-' || c == '+'))
c = getch();
if(c != '.' && !isdigit(c)) {
ungetch(c);
if(sawsign)
ungetch((sign == -1) ? '-' : '+');
return 0;
}
for(*pn = 0.0; isdigit(c); c = getch())
*pn = 10.0 * *pn + (float)(c - '0');
if(c == '.')
while(isdigit(c = getch())) {
*pn = 10.0 * *pn + (float)(c - '0');
power *= 10.0;
}
*pn *= sign;
*pn /= power;
ungetch(c);
return c;
}
It always returns 23.7999 when i enter 23.8, and i have no idea why. Can anybody tell me why?
Numbers are represented in base 2, and base-2 floating-point values cannot represent every base-10 decimal value exactly. What you enter as 23.8 gets converted into its closest equivalent base-2 value, which is not exactly 23.8. When you print this approximate value out, it gets printed as 23.7999.
You are also using float, which is the smallest floating-point type, and has only 24 bits of precision (roughly 7 decimal digits). If you switch to double, the amount of bits of precision more than doubles from float, so the difference between a decimal value such as 23.8 and its double representation is much smaller. This may allow a printing routine to perform the rounding better so that you see 23.8 with double. However, the actual value in the variable is still not exactly 23.8.
As general advice, unless you have a huge number of floating-point values (making memory usage your primary concern), it is best to use double whenever you need a floating-point type. You don't get rid of all odd behavior but you're going to see less of it than with float.
Because certain floating point numbers are inherently inaccurate.
23.8 can't be represented exactly given the limited accuracy of IEEE 754 floats.