The original article:https://tlanyan.pp.ua/c-cpp-keep-significant-figures/

The problem

My friend wanted to use a numerical example to verify the effect of rounding error on the stability of the algorithm. The first question I encountered in practice was: How can C/C++ keep only 4 significant figures?

If you’re used to languages like Java/JS, it’s very easy to keep a few decimal places. You can call functions like setScale, toFixed, etc., and specify a rounding pattern. But for C/C++, how do you keep the specified significant bits?

C/C++ reserves the specified significant number

In C/C++, the concept of precision appears only in the output (stream), so one way to implement this is to output a string with a specified precision and convert it to a number.

The C language specifies the number of significant digits

Let’s start with the C solution.

The C printf family of functions can specify the format of the output, which can be printed as a string and converted to a number by ATOF. The C version of specifying a significant number is as follows:

include <stdio.h>

include <stdlib.h>

double convert1(int precision, double val) {

char buffer\[128\];
sprintf(buffer, "%.*g", precision, val);

return atof(buffer);

}

Use code validation:

int main(int argc, char* argv[]) {

Double f1 = 1235.46698; printf("origin: %.12f, converted: %.12f\\n", f1, convert1(4, f1)); Double f2 = 1.23546698; printf("origin: %.12f, converted: %.12f\\n", f2, convert1(4, f2)); Double f3 = 0.00123546698; printf("origin: %.12f, converted: %.12f\\n", f3, convert1(4, f3)); Double f4 = 0.0000123546698; printf("origin: %.12f, converted: %.12f\\n", f4, convert1(4, f4)); return 0;

}

The output is as follows:

Origin: 1235.466980000000, converted: 1235.00000000000 Origin: 1.235466980000, x: 1.235000000000 Origin: 0.001235466980, converted: 0.001235000000 Origin: 0.000012354670, nil: 0.000012350000

4 significant digits were retained, which was in line with expectations.

The C++ language specifies the number of significant digits

C++ uses Generics to significantly refactor the standard library, making it more synthetically readable than C.

Here’s the C++ code that uses stringstream to convert numbers:

include <sstream>

template<int PRECISION>

double convert2(double input) {

std::stringstream is;
double res;
is.precision(PRECISION);
is << input;
is >> res;
return res;

}

Use code validation:

int main(int argc, char* argv[]) {

std::cout.precision(12);
std::cout.setf(std::ios::fixed, std:: ios::floatfield);

double f1 = 1235.46698;
std::cout << "origin: " << f1 << ", converted: " << convert2<4>(f1) << std::endl;

double f2 = 1.23546698;
std::cout << "origin: " << f2 << ", converted: " << convert2<4>(f2) << std::endl;

double f3 = 0.00123546698;
std::cout << "origin: " << f3 << ", converted: " << convert2<4>(f3) << std::endl;

double f4 = 0.0000123546698;
std::cout << "origin: " << f4 << ", converted: " << convert2<4>(f4) << std::endl;

return 0;

}

The output is consistent with the C language version.

conclusion

In C/C++, there is no native formatting support for basic types such as integers and floating-point numbers. Input and output formatting functions are needed to preserve the specified significant digits.

Similar problems with significant numbers include:

  1. How do I keep the number of decimal places specified in C/C++?
  2. How to implement FLOOR/CEILING, UP/DOWN, HALF\_UP/HALF\_DOWN Rounding modes in C/C++?

reference

  1. C++ Template Programming