You can't.
For example you can't round 0.31
to 1 decimal digit 0.3
as an double
with current HW, as the double
is not capable to store 0.30
exactly.
Try for example:
const double x = 0.3;
printf("%.20lf\n", x);
output:
0.29999999999999998890
(the exact value stored in x
is 0.299999999999999988897769753748434595763683319091796875
, encoded as 0x3fd3333333333333
64 bit integer value)
So you are just destroying precision bits of the original value, but the resulting value can still contain decimals with precision you don't expect.
For some more information study how the double
floating point numbers are designed, maybe this may be of help: https://en.wikipedia.org/wiki/Floating_point#Accuracy%5Fproblems
To get forward from this situation, there are two basic paths possible:
1) keep using HW floating point numbers
This has huge performance bonus, so that's why the float
/double
are first choice citizens in C++ floating point number calculations. Usually the tiny inaccuracy of results is small price to pay for the HW boost of calculations.
Then you should arrange your calculations in such way, that the cumulative error is smallest possible, and store around the double
values with maximum possible precision for any record purpose.
You then round the value to proper number of decimal places only during output formatting, for example like sprintf(char_buffer, "%.2lf", value);
to get the double
rounded to two decimal places in form of "string".
Of course you should be aware of total possible cumulative error, and make sure it's within the acceptable level of inaccuracy for your software.
2) use other encoding of your numbers: some big numbers library
One of the possibilities is to use some library for arbitrary precision [decimal] numbers (like Java's BigInteger
class, can't recall any C++ one from head).
These encode numbers in some custom binary way (or even as string), and have their own versions of common math operations, working with these numbers. Usually imitating human base 10 number format - which is not perfect either! For example you have two forms for every integer number: 1.0 = 0.9999...
, or numbers like Pi can't be written down without infinite number of decimal places.
These work like charm for financial software, where human base 10 formatting is actually perfect fit, with all it's quirks and imperfections.
The price is of course the performance, as each operation is emulated by several HW instructions, so even thing like simple addition may be 10-100 times slower than the HW double + double
.
3) use other encoding of your numbers: fixed math
When you need only exact number of decimal places, like for example you want uniform distribution of space coordinates, you can use integers, and assign some bits for decimal part. For example 16b unsigned short
can be split into 8:8 whole:decimal part, supporting numbers from 0.0 to 255.99609375 (255 + 255/256), with constant 256 (inclusive) granularity between every two integer values. It may look a bit confusing at first, but these are easy to use in ASM with bit shifting (to get the whole part of number you only shift the value by 8 bits to right, to add two values you simply add them as two integers, etc).
This is also sometimes used for financial software to calculate amounts with 2 decimal places, this is done not by allocating exact number of bits for the decimal part, but by simply having all values multiplied by 100, i.e. $3.59 is stored internally as 359
integer, and when the value is displayed to human, it is temporarily formatted as "$(value div 100).(value mod 100)"
.
The limitation of this method is, that you have to decide the fixed precision for particular values, so it's not universal silver bullet. But compared to the big number libraries, the fixed math has performance very close to HW floating point numbers, on some CPU architectures it may be actually faster (like back in ages Intel 80486).
So whatever you plan use that macro for, you should probably stop and look into your SW architecture, as it doesn't make sense.
If you want to use it for output formatting, I would suggest to use rather the already created and debugged C/C++ facilities, like printf
. If you still insist to do it by your own, at least produce some kind of string
, not double
, which would immediately destroy your rounding (as I shown at the beginning of my answer).
0.5
is incorrect. It does not produce correct results for a large number of floats. \$\endgroup\$Macro
. There is no reason to use such arcane tricks in modern C++ there are much better techniques (called functions). Are there betteryes
just looks a<cmath>
for rounding functionstrunc()
ceil()
floor()
round()
. All the difference specializations for rounding are already provided and implemented in the fastest way for your specific platform. All you would need to do is add the bit to cope with the number of digits. \$\endgroup\$