I am working on a small electronics project at home using a PIC microcontroller 18F which I am programming with HiTech C18 that is going to be used for digital control of a bench power supply.
I have run into a problem which is that I have a floating point number in a variable lets say for example 12.34 and need to split it out into 4 variables holding each individual number so i get Char1 = 1, Char2=2 etc etc for display on a 4-way seven segment LED display. The number will always be rounded to 2 decimal places so there shouldnt be a need to track the location of the decimal point.
I am trying to avoid any rounding where possible above 2 decimal places as the displays are giving measurements of voltage/current and this would affect the accuracy of the readouts.
Any advice on how to get this split would be greatly appreciated.
Thanks
Use sprintf to put the value into a character array. And then pick out the digits from there.
You could convert the floating point value directly to text. Or you could multiply by 100, truncate or round to int, and then convert that to text.
Convert to int and then to a string.
float x;
int i = x*100;
// or i = x*100.0f + 0.5f is round to nearest desired.
if ((i < 0) || (i > 9999)) Handle_RangeProblem();
char buf[5];
sprintf(buf, "%04d", i);
In embedded applications, many compilers use the fixed format string to determine which parts of the large printf() code will be needed. If code is all ready using "%f" else where, then a direct sprintf("%f") here is not an issue. Otherwise using %04d" could result in significant space savings.
Floating point numbers are stored in binary format comprised of a sign bit, mantissa, and exponent. A floating point number may not exactly match a given decimal representation (because of the different base-10 for decimal from the base-2 storage of floating point). Conversion of a floating point number to a decimal representation is a problem often assigned in beginning programming courses.
Since are only interested in two decimal places, and a limited range of values, you could use a fixed point representation of your value. This would reduce the problem from conversion of a floating point to decimal into conversion of integer to decimal.
long
longround( float f )
{
long x;
x = (long)((f*100)+.5); //round least significant digit
return(x);
}
char*
long2char( char ca[], long x )
{
int pos=0;
char sign = '+';
ca[pos] = '0';
long v = x;
if( v<0 ) {
sign = '-';
v = -v;
}
for( pos=0; v>0; ++pos )
{
ca[pos] = (v%10)+'0';
v = v/10;
}
ca[pos++] = sign;
ca[pos] = '\0'; //null-terminate char array
//reverse string - left as exercise for OP
return(ca);
}
If you have a problem where the largest value could exceed the range of values supported by long integer on your system, then you would need to modify the above solution.
Given the stated stability of your decimal point: simply sprintf() float into a buffer with appropriate format specifier, then you have your 4 values in a string easily extracted into what ever type you need them to be in...
Example
float num = 12.1234456;
char buf[6];
int main(void)
{
char a[2], b[2], c[2], d[2];
int x, y, z, w;
sprintf(buf, "%0.2f", num);//capture numeric into string
//split string into individual values (null terminate)
a[0] = buf[0]; a[1]=0;
b[0] = buf[1]; b[1]=0;
//skip decimal point
c[0] = buf[3]; c[1]=0;
d[0] = buf[4]; d[1]=0;
//convert back into numeric discretes if necessary
x = atoi(a);
y = atoi(b);
z = atoi(c);
w = atoi(d);
}
There are certainly more elegant ways, but this will work...
Related
Here's what I'm trying to do:
I need to print the fractional part of a floating number which has to be input as a float during user input.
The fractional part should be like: if float is 43.3423, the output should be 3423; and if number is 45.3400 output should be 3400.
This can be done easily with a string input but I need a way to make this work with float without losing the extra zeros or without appending zeros to user's original input.
Here's what I already tried :-
Take the fractional part by frac = num - (int)num and then multiplying frac until we get zero as the remainder. But this fails for cases like 34.3400 — the last two zeros won't get included with this method.
Convert the float number to a string by
char string[20];
sprintf(string, "%f", float_number);
The sprintf function puts the float number as a string but here also it doesn't automatically detect the user entered precision and fills the string with extra zeros at the end (6 total precision). So here also the information about the user's original entered precision is not obtained.
So, is there a way to get this done? The number must be taken as float number from user. Is there any way to get info about what's the user's entered precision? If it's not possible, an explanation would be very helpful.
I think I understand where you're coming from. E.g. in physics, it's a difference whether you write 42.5 or 42.500, the number of significant digits is implicitly given. 42.5 stands for any number x: 42.45 <= x < 42.55 and 42.500 for any x: 42.4995 <= x < 42.5005.
For larger numbers, you would use scientific notation: 1.0e6 would mean a number x with x: 950000 <= x < 1050000.
A floating point number uses this same format, but with binary digits (sometimes called bits ;)) instead of decimal digits. But there are two important differences:
The number of digits (bits) used depends only on the data type of the floating point number. If your data type has e.g. 20 bits for the mantissa, every number stored in it will have these 20 bits. The mantissa is always stored without a part after the "decimal" (binary?) point, so you won't know how many significant bits there are.
There's no direct mapping between bits and decimal digits. You will need roughly 3.5 bits to represent a decimal digit. So even if you knew a number of significant bits, you still wouldn't know how many significant decimal digits that would make.
To address your problem, you could store the number of significant digits yourself in something like this:
struct myNumber
{
double value;
int nsignificant;
};
Of course, you have to parse the input yourself to find out what to place in nsignificant. Also, use at least double here for the value, the very limited precision of float won't get you far. With this, you could use nsignificant to determine a proper format string for printing the number with the amount of digits you want.
This still has the problem mentioned above: you can't directly map decimal digits to bits, so there's never a guarantee your number can be stored with the precision you intend. In cases where an exact decimal representation is important, you'll want to use a different data type for that. C# provides one, but C doesn't. You'd have to implement it yourself. You could start with something like this:
struct myDecimal
{
long mantissa;
short exponent;
short nsignificant;
}
In this struct, you could e.g. place 1.0e6 like this:
struct myDecimal x = {
.mantissa = 1;
.exponent = 6;
.nsignificant = 2;
};
Of course, this would require you to write quite a lot of own code for parsing and formatting these numbers.
which has to be input as a float during user input.
So, is there a way to get this done.
Almost. The "trick" is to note the textual length of user input. The below will remember the offset of the first non-whitespace character and the offset after the numeric input.
scanf(" %n%f%n", &n1, &input, &n2);
n2 - n1 gives code the length of user input to represent the float. This method can get fooled if user input is in exponential notation, hexadecimal FP notation, infinity, Not-a-number, excessive leading zeros, etc. Yet works well with straight decimal input.
The idea is to print the number to a buffer with at least n2 - n1 precision and then determine how much of the fractional portion to print.
Recall that float typically has about 6-7 significant leading digits of significance, so attempting to input text like "123456789.0" will result in a float with the exact value of 123456792.0 and the output will be based on that value.
#include <float.h>
#include <math.h>
int scan_print_float(void) {
float input;
int n1, n2;
int cnt = scanf(" %n%f%n", &n1, &input, &n2);
if (cnt == 1) {
int len = n2 - n1;
char buf[len * 2 + 1];
snprintf(buf, sizeof buf, "%.*f", len, input);
char dp = '.';
char *p = strchr(buf, dp);
if (p) {
int front_to_dp = p + 1 - buf;
int prec = len - front_to_dp;
if (prec >= 0) {
return printf("<%.*s>\n", prec, p+1);
}
}
}
puts(".");
return 0;
}
int main(void) {
while (scan_print_float()) {
fflush(stdout);
}
return EXIT_SUCCESS;
}
Input/Output
43.3423
<3423>
45.3400
<3400>
-45.3400
<3400>
0.00
<00>
1234.500000
<500000>
.
.
To robustly handle this and the various edge cases, code should read user input as text and not as a float.
Note: float can typically represent about 232 numbers exactly.
43.3423 is usually not one of them. Instead it has an exactly value of 43.3423004150390625
43.3400 is usually not one of them. Instead it has an exactly value of 43.340000152587890625
The only way is to create a struct with the original string value and/ or required precision for rounding
I'm learning c, and am confused as my code seems to evaluate ( 1e16 - 1 >= 1e16 ) as true when it should be false. My code is below, it returns
9999999999999999 INVALIDBIG\n
when I would expect it not to return anything. I thought any problems with large numbers could be avoided by using long long.
int main(void)
{
long long z;
z = 9999999999999999;
if ( z >= 1e16 || z < 0 )
{
printf("%lli INVALIDBIG\n",z);
}
}
1e16 is a double type literal value, and floats/doubles can be imprecise for decimal arithmetic/comparison (just one of many common examples: decimal 0.2). Its going to cast the long-long z upwards to double for the comparison, and I'm guessing the standard double representation can't store the precision needed (maybe someone else can demonstrate the binary mantissa/sign representations)
Try changing the 1e16 to (long double)1e16, it doesn't then print out your message. (update: or, as the other question-commenter added, change 1e16 to an integer literal)
The doubles and floats can hold limited number of digits. In your case the double numbers with values 9999999999999999 and 1e16 have identical 8 bytes of hex representation. You can check them byte by byte:
long long z = 9999999999999999;
double dz1 = z;
double dz2 = 1e16;
/* prints 0 */
printf("memcmp: %d\n", memcmp(&dz1, &dz2, sizeof(double)));
So, they are equal.
Smaller integers can be stored in double with perfect precision. For example, see Double-precision floating-point format or biggest integer that can be stored in a double
The maximum integer that can be converted to double exactly is 253 (9007199254740992).
I have a sine equation which return a float value like:
0.0034,.000006,6.1684332059801899768737349875082e-4.
I want to convert the corresponding float value to integer variables and pass it to a register to generate the particular sine wave graph.
Please help me out.
If you're getting values like 0.0034, you can't simply cast that to an int since it will come out as zero. In fact, the majority of floating point values between zero and one will come out as zero.
You need to first scale up the value, then cast it to an integer.
For example, to turn the output of a sine function into a value between -100 and 100, you could use something like:
int val = (int)(fpVal * 100);
That will turn the range (-1,1) (but mostly 0) into something more usable, (-100,100) (with slightly better distribution across the range).
You may also want to round the value rather than truncate it, to ensure the values generated more accurate represent the inputs.
You could try to save the float value into 3 unsigned integers, one for the values left of the decimal point and two for the values on the right of the decimal point.
For example, we get the float number "359.042042" and save it as variable flt.
int main(void)
{
unsigned int int1, int2, int0;
float flt = 359.042042;
int1 = floor(flt);
int2 = (flt - int1) * pow(10.0, 8.0);
int0 = 8 - log10(int2);
printf("%d.", int1);
while (int0 != 0)
{
printf("0");
int0--;
}
printf("%d", int2);
return 0;
}
int1 are the digits on the left side of the decimal points, int2 are the digits on the right and int0 are the "0" before int2.
This would print "359.042042". Good luck.
I'm trying to write a code that converts a real number to a 64 bit floating point binary. In order to do this, the user inputs a real number (for example, 547.4242) and the program must output a 64 bit floating point binary.
My ideas:
The sign part is easy.
The program converts the integer part (547 for the previous example) and stores the result in an int variable. Then, the program converts the fractional part (.4242 for the previous example) and stores the result into an array (each position of the array stores '1' or '0').
This is where I'm stuck. Summarizing, I have: "Integer part = 1000100011" (type int) and "Fractional part = 0110110010011000010111110000011011110110100101000100" (array).
How can I proceed?
the following code is used to determine internal representation of a floating point number according to the IEEE754 notation. This code is made in Turbo c++ ide but you can easily convert for a generalised ide.
#include<conio.h>
#include<stdio.h>
void decimal_to_binary(unsigned char);
union u
{
float f;
char c;
};
int main()
{
int i;
char*ptr;
union u a;
clrscr();
printf("ENTER THE FLOATING POINT NUMBER : \n");
scanf("%f",&a.f);
ptr=&a.c+sizeof(float);
for(i=0;i<sizeof(float);i++)
{
ptr--;
decimal_to_binary(*ptr);
}
getch();
return 0;
}
void decimal_to_binary(unsigned char n)
{
int arr[8];
int i;
//printf("n = %u ",n);
for(i=7;i>=0;i--)
{
if(n%2==0)
arr[i]=0;
else
arr[i]=1;
n/=2;
}
for(i=0;i<8;i++)
printf("%d",arr[i]);
printf(" ");
}
For further details visit Click here!
In order to correctly round all possible decimal representations to the nearest double, you need big integers. Using only the basic integer types from C will leave you to re-implement big integer arithmetics. Each of these two approaches is possible, more information about each follows:
For the first approach, you need a big integer library: GMP is a good one. Armed with such a big integer library, you tackle an input such as the example 123.456E78 as the integer 123456 * 1075 and start wondering what values M in [253 … 254) and P in [-1022 … 1023] make (M / 253) * 2P closest to this number. This question can be answered with big integer operations, following the steps described in this blog post (summary: first determine P. Then use a division to compute M). A complete implementation must take care of subnormal numbers and infinities (inf is the correct result to return for any decimal representation of a number that would have an exponent larger than +1023).
The second approach, if you do not want to include or implement a full general-purpose big integer library, still requires a few basic operations to be implemented on arrays of C integers representing large numbers. The function decfloat() in this implementation represents large numbers in base 109 because that simplifies the conversion from the initial decimal representation to the internal representation as an array x of uint32_t.
Following is a basic conversion. Enough to get OP started.
OP's "integer part of real number" --> int is far too limiting. Better to simply convert the entire string to a large integer like uintmax_t. Note the decimal point '.' and account for overflow while scanning.
This code does not handle exponents nor negative numbers. It may be off in the the last bit or so due to limited integer ui or the the final num = ui * pow10(expo). It handles most overflow cases.
#include <inttypes.h>
double my_atof(const char *src) {
uintmax_t ui = 0;
int dp = '.';
size_t dpi;
size_t i = 0;
size_t toobig = 0;
int ch;
for (i = 0; (ch = (unsigned char) src[i]) != '\0'; i++) {
if (ch == dp) {
dp = '\0'; // only get 1 dp
dpi = i;
continue;
}
if (!isdigit(ch)) {
break; // illegal character
}
ch -= '0';
// detect overflow
if (toobig ||
(ui >= UINTMAX_MAX / 10 &&
(ui > UINTMAX_MAX / 10 || ch > UINTMAX_MAX % 10))) {
toobig++;
continue;
}
ui = ui * 10 + ch;
}
intmax_t expo = toobig;
if (dp == '\0') {
expo -= i - dpi - 1;
}
double num;
if (expo < 0) {
// slightly more precise than: num = ui * pow10(expo);
num = ui / pow10(-expo);
} else {
num = ui * pow10(expo);
}
return num;
}
The trick is to treat the value as an integer, so read your 547.4242 as an unsigned long long (ie 64-bits or more), ie 5474242, counting the number of digits after the '.', in this case 4. Now you have a value which is 10^4 bigger than it should be. So you float the 5474242 (as a double, or long double) and divide by 10^4.
Decimal to binary conversion is deceptively simple. When you have more bits than the float will hold, then it will have to round. More fun occurs when you have more digits than a 64-bit integer will hold -- noting that trailing zeros are special -- and you have to decide whether to round or not (and what rounding occurs when you float). Then there's dealing with an E+/-99. Then when you do the eventual division (or multiplication) by 10^n, you have (a) another potential rounding, and (b) the issue that large 10^n are not exactly represented in your floating point -- which is another source of error. (And for E+/-99 forms, you may need upto and a little beyond 10^300 for the final step.)
Enjoy !
I tried following code snippet and output is surprising me:
#include <stdio.h>
#include <math.h>
int main()
{
double num;
unsigned char ch;
ch = 19;
num = 1.0E+20 ;
num += ch * 1.0E+18;
printf("E18 = %lf \n",num);
printf("E18 = %e \n",num);
num = 11.0E+21 ;
num += ch * 1.0E+19;
printf("E19 = %lf <------\n",num);
printf("E19 = %e <------\n",num);
num = 11.0E+22 ;
num += ch * 1.0E+20;
printf("E20 = %lf\n",num);
printf("E20 = %e\n",num);
num = 11.0E+23 ;
num += ch * 1.0E+21;
printf("E21 = %lf\n",num);
printf("E21 = %e\n",num);
num = 11.0E+24 ;
num += ch * 1.0E+22;
printf("E22 = %lf <------\n",num);
printf("E22 = %e <------\n",num);
return 0;
}
The output of the program:
E18 = 119000000000000000000.000000
E18 = 1.190000e+20
E19 = 11190000000000000524288.000000 <------
E19 = 1.119000e+22 <------
E20 = 111900000000000001048576.000000
E20 = 1.119000e+23
E21 = 1119000000000000044040192.000000
E21 = 1.119000e+24
E22 = 11189999999999999366660096.000000 <------
E22 = 1.119000e+25 <------
Why the data corrupted when printed while in exponent form its OK
Because, you lose precision when the numbers grow big enough : http://en.wikipedia.org/wiki/Floating_point
The data is not corrupted; that's simply how floating point values work in today's computers. Think of a float as a fixed number of digits (the mantissa) and another number that indicates where the decimal point should be placed (the exponent). For the long and more accurate story, Wikipedia is a good start.
Since the number of digits of the mantissa is fixed, it cannot represent the tiny fraction that you're asking for here. Furthermore, because it's all in binary, decimal numbers cannot always be represented exactly.
The exponential notation simply rounds off the last few digits where the number is known to be inaccurate, hence your results.
Double/floating-point numbers lose precision as they get larger - in addition to the Wikipedia article Tuomas has posted, here's another good one:
http://www.yoda.arachsys.com/csharp/floatingpoint.html
It was targeted at .NET but the principle still applies.
Floating point data types use a finite number of bits to represent a certain range of numbers. However, there can be an infinite number of values between any two real numbers m and n. So, you sacrifice precision.
It looks alright in exponent form since not all digits are being printed.
As a test, try printing the value 0.2 with about 10 decimal places and you will see that the value stored is more like 0.1999999....
You are seeing the imprecision inherent in floating point.
Certain printf conversions are guaranteed to produce enough significant figures to uniquely identify the number being printed. This implies that if there is any imprecision, you will see it. Conversely, the default %e/%f representation hides the imprecision by rounding.
As far as I know, %a (hexadecimal floating point) is the only way to achieve this. According to the POSIX spec, %lf is defined to do the same thing as %f, that is,
l (ell) … has no effect on a following a , A , e , E , f , F , g , or G conversion specifier.
So this is technically a bug in your standard library.