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".
Related
I'm trying to print out the decimal expansion of a rational number in C. The problem I have is that when I divide the numerator by the denominator I lose precision. C rounds up the repeating part when I don't want it to.
For example, 1562/4995 = 0.3127127127... but in my program I get 1562/4995 = 0.312713. As you can see a part of the number that I need has been lost.
Is there a way to specify C to preserve a higher level of decimal precision?
I have tried to declare the result as a double, long double and float. I also tried to split the expansion into 2 integers seperated by a '.'
However both methods haven't been successful.
int main() {
int numerator, denominator;
numerator = 1562;
denominator = 4995;
double result;
result = (double) numerator / (double) denominator;
printf("%f\n", result);
return 0;
}
I expected the output to be 1562/4995 = 0.3127127127... but the actual output is 1562/4995 = 0.312713
The %f format specifier to printf shows 6 digits after the decimal point by default. If you want to show more digits, use a precision specifier:
printf("%.10f\n", result);
Also, the double type can only accurately store roughly 16 decimal digits of precision.
You need to change your output format, like this:
printf("%.10lf\n", result);
Note two things:
The value after the . specifies the decimal precision required (10 decimal places, here).
Note that I have added an l before the f to explicitly state that the argument is a double rather than a (single-precision) float.
EDIT: Note that, for the printf function, it is not strictly necessary to include the l modifier. However, when you come to use the 'corresponding' scanf function for input, it's absence will generate a warning and (probably) undefined behaviour:
scanf("%f", &result); // Not correct
scanf("%lf", &result); // Correct
If printing is all you want to do, you can do this with the same long division you were taught in elementary school:
#include <stdio.h>
/* Print the decimal representation of N/D with up to
P digits after the decimal point.
*/
#define P 60
static void PrintDecimal(unsigned N, unsigned D)
{
// Print the integer portion.
printf("%u.", N/D);
// Take the remainder.
N %= D;
for (int i = 0; i < P && N; ++i)
{
// Move to next digit position and print next digit.
N *= 10;
printf("%u", N/D);
// Take the remainder.
N %= D;
}
}
int main(void)
{
PrintDecimal(1562, 4995);
putchar('\n');
}
If I convert the decimal number 3120.0005 to float (32-bit) representation, the number gets rounded down to 3120.00048828125.
Assuming we're using a fixed point number with a scale of 10^12 then 1000000000000 = 1.0 and 3120000500000000 = 3120.0005.
What would the formula/algorithm be to round down to the nearest IEEE 754 precision to get 3120000488281250?
I would also need a way to get the result of rounding up (3120000732421875).
If you divide by the decimal scaling factor, you'll find your nearest representable float. For rounding the other direction, std::nextafter can be used:
#include <float.h>
#include <math.h>
#include <stdio.h>
long long scale_to_fixed(float f)
{
float intf = truncf(f);
long long result = 1000000000000LL;
result *= (long long)intf;
result += round((f - intf) * 1.0e12);
return result;
}
/* not needed, always good enough to use (float)(n / 1.0e12) */
float scale_from_fixed(long long n)
{
float result = (n % 1000000000000LL) / 1.0e12;
result += n / 1000000000000LL;
return result;
}
int main()
{
long long x = 3120000500000000;
float x_reduced = scale_from_fixed(x);
long long y1 = scale_to_fixed(x_reduced);
long long yfloor = y1, yceil = y1;
if (y1 < x) {
yceil = scale_to_fixed(nextafterf(x_reduced, FLT_MAX));
}
else if (y1 > x) {
yfloor = scale_to_fixed(nextafterf(x_reduced, -FLT_MAX));
}
printf("%lld\n%lld\n%lld\n", yfloor, x, yceil);
}
Results:
3120000488281250
3120000500000000
3120000732421875
In order to handle the values as float scaled by 1e12 and compute the next larger power of two, e.g. "rounding up (3120000732421875)", the key is understanding that you are looking for the next larger power of two from the 32-bit representation of x / 1.0e12. While you can mathematically arrive at this value, a union between float and unsigned (or uint32_t) provides a direct way to interpret the stored 32-bit value for the floating-point number as an unsigned value.1
A simple example utilizing a the union prev to hold the reduced value of x and a separate instance next holding the unsigned value (+1) can be:
#include <stdio.h>
#include <inttypes.h>
int main (void) {
uint64_t x = 3120000500000000;
union { /* union between float and uint32_t */
float f;
uint32_t u;
} prev = { .f = x / 1.0e12 }, /* x reduced to float, pwr of 2 as .u */
next = { .u = prev.u + 1u }; /* 2nd union, increment pwr of 2 by 1 */
printf ("prev : %" PRIu64 "\n x : %" PRIu64 "\nnext : %" PRIu64 "\n",
(uint64_t)(prev.f * 1e12), x, (uint64_t)(next.f * 1e12));
}
Example Use/Output
$ ./bin/pwr2_prev_next
prev : 3120000488281250
x : 3120000500000000
next : 3120000732421875
Footnotes:
1. As an alternative, you can use a pointer to char to hold the address of the floating point type and interpret the 4-byte value stored at that location as unsigned without running afoul of C11 Standard - §6.5 Expressions (p6,7) (the "Strict Aliasing Rule"), but the use of a union is preferred.
If I have 2.55, how do I write only .55 and skip 2 in programming language?
Well you can do this to store it in another variable -
double a=2.55,b;
b =a-(long)a; // subtracting decimal part from a
printf("%.2f\n",b);
As pointed out by Mark Dickinson Sir in comment that this is not safe . So you can make use of function modf from <math.h>-
For example -
double a=-2.55,b,i;
b =modf(a,&i); // i will give integer part and b will give fraction part
printf("%.2f\n",b);
Use double modf(double value, double *iptr) to get the factional part. Use round() to get the best value near the requested precision.
double GetDecimalPlaces(double x, unsigned places) {
double ipart;
double fraction = modf(x, &ipart);
return fraction;
// or
double scale = pow(10.0, places);
return round(fraction * scale)/scale;
}
void GetDecimalPlaces_Test(double x, unsigned places) {
printf("x:%e places:%u -->", x, places);
printf("%#.*f\n", places, GetDecimalPlaces(x, places));
// Additional work needed if leading '0' is not desired.
}
int main(void) {
GetDecimalPlaces_Test(2.55, 2);
GetDecimalPlaces_Test(-2.55, 2);
GetDecimalPlaces_Test(2.05, 2);
GetDecimalPlaces_Test(0.0, 2);
GetDecimalPlaces_Test(0.0005, 2);
}
Output
x:2.550000e+00 places:2 -->0.55
x:-2.550000e+00 places:2 -->-0.55
x:2.050000e+00 places:2 -->0.05
x:0.000000e+00 places:2 -->0.00
x:5.000000e-04 places:2 -->0.00
One dirty trick is to cast your double to an int to get only the whole number. You can then subtract the two to get only the decimal part:
double d = 2.55;
double remainder = d - (int)d;
printf ("%.2f\n", remainder);
double values are not perfectly precise, so small rounding errors can get introduced. You can store the total number in an Integer. You can for example divide by 100 to get the value before the . and use % modulus to get the decimal values.
Example:
int main()
{
int num = 255;
printf("%d.%d\n", num / 100, num % 100); // prints 2.55
printf(".%d", num % 100); // prints .55
return 0;
}
This fails with negative numbers, but you can easily add cases to handle that.
I Have this "simple" code.
union
{
unsigned int res;
char bytes[2];
} ADC;
char ADC_num[5];
float temprature;
void vis_temp (void) // Show temp..
{
signed int l, length;
unsigned int rem;
GO_nDONE=1; // initiate conversion on the channel 0
while (GO_nDONE) continue;
ADC.bytes[0]=ADRESL;
ADC.bytes[1]=ADRESH;
utoa(ADC_num, ADC.res, 10);
temprature = (float) ADC.res * 478.1 / 1024;
temprature = temprature - 50.0;
l = (signed int) temprature;
temprature -= (float) l;
rem = (unsigned int)(temprature* 1e1);
sprintf(&ADC_num, "%i.%u", l, rem);
When reading ADC_res (voltage on pin, temperature sensor) that temperature is 0 degree or under then program writes "0.65500" instead of "-3.5" or similar.
I should have declared the right as signed and unsigned int.
Any hints to fix it, or have an other way of converting it.
temprature = (float) ADC.res * 478.1 / 1024;
temprature = temprature - 50.0;
Suppose now temprature has a negative value -x.yz.
l = (signed int) temprature;
Now l = -x, and
temprature -= (float) l;
temprature = -x.yz - (-x) = -0.yz.
rem = (unsigned int)(temprature* 1e1);
Multiply with 10, and convert to unsigned int. Usually, that results in undefined behaviour (6.3.1.4 (1)):
When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined.61)
61) The remaindering operation performed when a value of integer type is converted to unsigned type need not be performed when a value of real floating type is converted to unsigned type. Thus, the range of portable real floating values is (−1, Utype_MAX+1).
But converting the negative value to unsigned int would produce the wrong result anyway, even if the remaindering operation is done, what you want is the absolute value, so you should convert
rem = (unsigned int)fabsf(temprature * 1e1);
there.
I think the problem may be coming from the call to the utoa() function.
The utoa() function prototype is generally as follow
char * utoa(unsigned int n, char * buffer, int radix);
In your code you have inverted the two first parameters. You are modifying the ADC structure through this call. I am curious how this could ever be compiled without any error? Any decent compiler would complain about passing an argument which is not a pointer type.
Try with the following
utoa(ADC.res, ADC_num, 10);
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.