Reputation: 93
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;}
Upvotes: 2
Views: 174
Reputation: 153303
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
Upvotes: 0
Reputation: 51393
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
.
Upvotes: 2
Reputation: 51815
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;
}
Upvotes: 0
Reputation: 1415
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 ;)
Upvotes: 0