I have the following C code to determine the decimal places of a double value. It is using the sprintf()'s %f for ending up with a string without floating point imprecision. Is there a risk that sprintf() is not eliminating the floating point imprecision correctly, or is that function safe to use?
#include <stdio.h>
#include <stdbool.h>
#define MAX_STRING 256
int getDecimalPlaces(double n)
{
char string[MAX_STRING];
int numberOfDecimalPlaces = 0;
int i = sprintf(string, "%f", n);
bool countZero = false;
for (int j = i - 1; j > 0; j--)
{
if (string[j] == '.')
break;
if (!countZero && string[j] != 48)
countZero = true;
if (countZero)
numberOfDecimalPlaces ;
}
return numberOfDecimalPlaces;
}
I call the function for numbers to perform a mathematical operation with them (least common multiply). I need whole numbers for this operation so that I am searching for the number with the most decimal places. Then I am multiplying each number by 10 ^ (most decimal places). The numbers dealt with are user-entered and belong to a linear system of equations. In most cases, 2 decimal places are enough.
The numbers itself don't have to be very precise, but their decimal places have to match up with what a user entered.
CodePudding user response:
Your function only produces 6 decimals for any finite number, so it cannot return a number of decimals greater than 6 and the number of decimals returned can be much smaller than anticipated: if the value is not an integer but close enough, the function will return 0, eg: getDecimalPlaces(1.0000004) or getDecimalPlaces(1.9999996).
%f formats the number with a fixed number of decimals, which you can specify with a precision field, such as %.10f. It defaults to 6.
If you want to determine the number of decimals needed to represent the value n unambiguously, you should use 20.18g and handle the exponent part that may be produced for values between -1 and 1.
Note, however, that the double type uses a binary representation for floating point values internally. Unless the value is an exact multiple of a negative power of 2, the representation is an approximation of its decimal representation.
For example 1.5 is represented exactly, without approximation, but 0.1 is not, just like 1/3 cannot be represented exactly using a decimal representation.
The internal representation of 0.1 is approximate, but depending on the implementation of sprintf(), converting this approximate value to decimal can produce 0.1000000000000000000013552527156068805425093160010874271392822265625 or just 0.1, as both will convert back to the same value.
sprintf is not safe to use as coded in your function: for a very large number, greater than 1e250, snprintf will produce more than 256 characters and write beyond the end of the array. It is much safer to use snprinf.
If your goal is to convert the value in decimal form with at most 6 decimals and no exponent, you can change you can use this modified version:
#include <stdio.h>
int convertValue(char *dest, size_t size, double d, int maxplaces) {
int len = snprintf(dest, "%.*f", maxplaces, d);
while (n > 0 && string[len - 1] == '0')
string[--len] = '\0';
if (len > 0 && string[len - 1] == '.')
string[--len] = '\0';
return len;
}
EDIT: for your purpose, you want to convert the numbers to integers. Make sure you use round(d * power_of_10) to ensure proper conversion. Without the round(), you might get incorrect conversions, such as 0.7 * 10 converting to 6 instead of 7.
