I have written a function that converts a double to a BCD (BCD: Save each digit of the double as an unsigned char, in addition save the complete length, the fractional length (part behind the floating point) and the sign of the double number).
I use the following struct
struct bcd_number
{
unsigned int length;
unsigned int fractional;
signed char sign;
unsigned char *digits;
};
And thats the double to BCD function:
struct bcd_number* double2bcd(double x)
{
char tmp[512];
struct bcd_number* bcd = malloc (sizeof(struct bcd_number));
int a = x;
double before = a;
double fractional;
fractional = x-(long)x;
bcd->digits = malloc (512);
char* z = (char*) bcd->digits;
sprintf (tmp,"%g",fabs(before));
bcd->length = strlen(tmp);
bcd->sign = (before < 0) ? '-' : '+';
for (size_t i=0; i<bcd->length; ++i)
{ *z++ = tmp[i] - '0'; }
sprintf (tmp,"%g",fabs(fractional));
for (size_t i = strlen(tmp)-1; i!=0; --i)
if (tmp[i] != '0')
{ tmp[i+1] = 0; break; }
bcd->fractional = strlen(tmp+2);
bcd->length += bcd->fractional;
for (char* t = tmp + 2; *t; *z++ = *t++ - '0');
bcd->digits = realloc (bcd->digits, bcd->length);
return bcd;
}
That works perfect.
And I had also added the ability to perform addition/subtraction (Complete source code: http://pastebin.com/HR70VukM) but now I want to perform multiplication and division.
But the problem is that there are only chars as digits (I don't want to change that). I now that must be like 'multiplication on the paper' (classical way without calculator) but I have the idea that it must be like addition with the modulo operator. On the other hand I have no idea how to implement it with chars with modulo. Any ideas or hints?
what comes after multiplication and division? factorial? modulus? exponent? natural logarithm? sine? cosine?
turn the BCDs back into doubles, do whatever math operation, turn the result in BCD
Everything you ever wanted to know about BCD can be found at the General Decimal Arithmetic web site.
For multiplication, you will need a primitive routine that multiples two digits together yielding a two digit result. Add this intermediate result to the appropriate position in the answer. Besides having a "multiplication table," finding this "appropriate position" is the crux of "the same you'd do it by hand" method.
Related
I want to write code that, if I input a decimal number like 612.216, I can print it as a 612216 (actually convert it to integer). However, the program changes my number to something like 2162160000000000000000001 and I don't what to do about it.
This is my code:
#include <stdio.h>
#include <math.h>
int main() {
long double x;
scanf_s("%Lf", &x);
while (floor(x)!=x)
x = x * 10;
printf("%Lf", x);
return 0;}
How about this:
#include <stdio.h>
int main() {
double number = 612.216;
char number_as_string[20];
snprintf(number_as_string,"%lf", number);
for(int i = 0; number_as_string[i] != '\0'; i++)
if(number_as_string[i] != '.')
printf("%c", number_as_string[i]);
return 0;
}
The downside is the statically allocated array. You can use snprintf to convert the double into an array of chars.
The floating point representation isn't exact so there is a very very small error in any floating point number. You could try something like this pseudocode,
while ((x - floor(x) > 0.0000000000000000001)
x *= 10;
Your math library might define a better number to use like FLT_MIN or some such ;)
The problem with your floor(x)!=x check is that it doesn't take into account any inaccuracy in the representation of the input long double number. (In the example given, this causes an 'extra' 0.0000000000000000000001 to be added to the actual value.) See Is floating point math broken? for more information on such inaccuracies inherent in any representation of floating-point numbers.
To fix this in your code, you can compare the difference between floor(x) and x to a given 'tolerance' - if it's less than that, consider the loop finished. You can use a value derived from the LDBL_EPSILON constant as a typical value for that 'tolerance', though you may like to experiment with different values.
Here is a possible code solution:
#include <stdio.h>
#include <math.h>
#include <float.h> // For the LDBL_EPSILON definition
int main()
{
long double x;
scanf_s("%Lf", &x);
while ((x - floor(x)) > (LDBL_EPSILON * x * 10)) // Try changing the "10" value!
x = x * 10;
printf("%.0Lf", x); // Add the ".0" to remove the trailing ".000000" in the output
return 0;
}
long double can store many finite values exactly. There are all of the form:
+/- some_integer * 2some_exponent
Since "612.216" is not represent-able like that (0.216 cannot be expressed as a binary fraction like 0.25 can), a nearby long double value was used like ~612.2160000000000000253...
Also, OP's repeated use of x = x * 10; adds small rounding errors and does not pose a reasonable conversion limit.
A alternative approach uses LDBL_DIG (the number of significant decimal digits that round trip from decimal text to long double to decimal text unchanged) and to print the double to a buffer. Let *printf() do the heavy lifting of converting a double to the best decimal text.
#include <float.h>
#include <stdio.h>
// To print up to the LDBL_DIG most significant digits w/o trailing zeros:
void print_sig_digits(long double x) {
// - d . ddd....ddd e - expo \0
char buf[1 + 1 + 1 + (LDBL_DIG-1) + 1 + 1 + 8 +1];
#define e_OFFSET (1 + 1 + 1 + (LDBL_DIG-1))
// Print using exponential format
snprintf(buf, sizeof buf, "%+.*Le", LDBL_DIG, x);
buf[e_OFFSET] = '\0'; // End string at 'e'
for (int i = e_OFFSET - 1; buf[i] == '0'; i--) {
buf[i] = '\0'; // Lop off trailing '0'
}
buf[2] = buf[1]; // Copy first digit over '.'
printf("%s\n", &buf[2]);
}
int main(void) {
printf("LDBL_DIG: %d\n", LDBL_DIG);
print_sig_digits( 612.216L);
print_sig_digits( 1.0L/7);
print_sig_digits( 0.000123L);
return 0;
}
Output
LDBL_DIG: 18
612216
142857142857142857
123
I have to round off a float to decimal. After rounding off, I should convert this number to hexadecimal. I think I got the round off part okay with round()
Is there a way to convert a decimal to hexadecimal in C, and store it into a part of an array?
I'm thinking of the concept on how printf() converts the decimal to hex.
What I have in mind is something like this:
float k = 10.123;
int a;
unsigned char var_store[1];
unsigned char array_t[3];
array_t[0] = 0x01;
array_t[1] = 0x04;
a = round(k);
var_store[0] = sprintf("%x",a);
array_t[2] = var_store[0];
but I'm having a
warning passing argument 2 of 'sprintf' makes pointer from integer
without a cast
I'm not sure if this is the way to do it. But I think this is relatively straight forward. Thanks
People tend to get very confused with the term "hexadecimal". It should mean "the number as a human-readable ascii string with digits 0-F", but because raw binary data is typically presented in hex, people miuse it to mean the binary data itself.
Whilst of course you can write a function that converts a decimal number, expressed as a string, to a hexadecimal number, expressed as another string, it's fiddly and, except as a learning exercise, pointless thing to do. sprintf converts C variables to human-readable strings for you. To get a decimal, pass "%d", to get hex, pass "%x". You also need to pass a destination buffer, like this.
char destination[256];
int a = 123;
sprintf(destination, "number is decimal %d hex %x", a, a);
I did not recollect any library function.
But the traditional mathematical way is below. I you want you can create a user defined function.
#include <iostream>
using namespace std;
int main()
{
long int decimalNumber = 2567888;
char hexadecimalNumber[100];
int temp;
int i =1;
while(decimalNumber!=0)
{
temp = decimalNumber % 16;
//To convert integer into character
if( temp < 10)
temp =temp + 48;
else
temp = temp + 55;
hexadecimalNumber[i++]= temp;
decimalNumber = decimalNumber / 16;
}
for(int j = i -1 ;j> 0;j--)
cout<<hexadecimalNumber[j];
}
I am trying to code my printf() function. I wanted to print float/double values. This is what I managed to do so far.
static void ft_float(va_list *ap, t_flag *flags)
{
double myfloat;
signed long int decipart;
signed long int intpart;
myfloat = va_arg(*ap, double);
if (myfloat < 0)
{
ft_myputchar('-');
myfloat *= -1;
}
intpart = (signed long int)myfloat;
ft_putnbr(intpart);
ft_myputchar('.');
myfloat -= intpart;
myfloat *= 1000000; //upto 6 decimal points
decipart = (signed long int)(myfloat + 0.5); //+0.5 to round of the value
ft_putnbr(decipart);
}
As you can see for obvious reasons the code works good for floats like 1.424352, 12313.1341414 etc. But not when the value after the decimal point is less than 1, for example 1.004243, 12313.0001341 etc.
Function printf can be used to pad the value with zeroes. Flag 0 sets the padding to be 0, and the width specifies the minimum number of characters written, padding at the left side is used if necessary.
Simply printf decipart with the format: "%06ld".
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 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...