Introduction
In general, C has a rather powerful way of printing different values; for example, using print patterns as 1.3lf
in the example below:
double value = 1.543605908721;
printf("Value 1.3lf", value);
The only problem with this approach is that this way of defining the printing format is absolutely non-flexible.
What if one wants to print values in different formats, depending on the input of the program itself?
In this paper, we will discuss this problem in some detail.
Originally, the article was inspired by the problem of printing probability values up to the "right digit" after the decimal point. The "right digit" is the one that corresponds to a pre-set error bound.
All examples below are adapted from Markov Reward Model Checker (MRMC) tool sources, which are written by me and are freely distributed under the GPL license.
The Task
Having an array of double
values, probs
, and an error bound, error_bound
(the same for all array elements), we would like to print those values, excluding all digits after the decimal point that are insignificant, with respect to the error bound.
For example:
double * probs = {0.565820439, 0.33339};
double error_bound = 1.0e-3;
Note that the output above is made up to the 4th digit after the decimal point but not up to the 3rd as one would be expecting. The reason is that, for example, for values 0.565820439
and 0.564710439
which have a difference 1.1e-3
(greater than the given error_bound
), printing them up to the 3rd digit after the decimal point would not let us recognize this difference: 0.565
and 0.564
.
Solution
The main task, as surprising as it could be, is to compute up to which digit after the decimal point the values should be printed. Consider the following function that computes it:
int get_error_bound_precision(double error_bound){
int precision = 0;
BOOL not_found = TRUE;
double integer = 0, fraction = 0;
while ( not_found ){
error_bound *= 10;
fraction = modf(error_bound, &integer);
if ( integer > 0.0 ){
not_found = FALSE;
}
precision++;
}
return precision + 1;
}
What is done here, is basically going down the digits of the error_bound
and looking for the first non zero one.
The rest is fairly easy, we have to create the pattern for printing our double
values, which is done by using the sprintf
operator, see the function below:
void print_state_probs(int size, double * probs, double error_bound ){
printf("Result: ");
char buffer[255];
sprintf(buffer, "%%1.%dlf", get_error_bound_precision(error_bound));
print_pattern_vec_double(buffer, size, probs);
}
and then call the following simple function that would print the array of double
s using the pattern we derived earlier.
void print_pattern_vec_double(const char * pattern, int length, double * vec)
{
int i;
printf("( ");
for( i = 0 ; i < length ; i++ ){
printf( pattern, vec[i] );
if (i != length-1) printf(", ");
}
printf(" )\n");
}
Conclusion
Here, we considered one of the simplest ways to print double
values with the precision defined by some error bound.
Below, I will mention two more ways of solving the problem, which I've considered during my investigation:
- Having
double error_bound = 1.47003454e-10
and printing values with the desired precision, taking into account the last non-zero digit of error_bound
, is not feasible because actually the value stored in the double error_bound
variable is not exactly 1.47003454e-10
but is something like 1.47003454000000000000329999961234769e-10
, which is the desired error plus some noise. - Using the IEEE floating-point standard to compute the desired precision from
error_bound
, does not really help. From the first glance, there is a part that gives you some knowledge about the exponential part of the number, but it is all about the power of 2, whereas we are interested in the power of 10
.