'Round 37.1-28.75 float calculation correctly to 8.4 instead of 8.3

I have problem with floating point rounding. I want to calculate floating point numbers and round them to (given) N decimals. In this example I want to round to 1 decimal places.

Calculation 37.1-28.75 will result into floating point 8.349998 (instead of 8.35), which will result printf rounding to 8.3 instead of 8.4 for 1 decimal places.

The actual result in math is 37.10-28.75=8.35000000, but due to floating point imprecision it is converted into 8.349998, which is then converted into 8.3 instead of 8.4 when using 1 decimal place rounding.

Minimum reproducible example:

float a = 37.10;
float b = 28.75;
//a-b = 8.35 = 8.4
printf("%.1f\n", a - b); //outputs 8.3 instead of 8.4

Is it valid to add following to the result:

float result = a - b;

if (result > 0.0f)
{
    result += powf(10, -nr_of_decimals - 1) / 2;
}
else
{
    result -= powf(10, -nr_of_decimals - 1) / 2;
}

EDIT: corrected that I want 1 decimal place rounded output, not 2 decimal places

EDIT2: negative results are needed as well (28.75-37.1 = -8.4)



Solution 1:[1]

On my system I do actually get 8.35. It's possible that you have to set the rounding direction to "nearest" first, try this (compile with e.g. gcc ... -lm):

#include <fenv.h>
#include <stdio.h>

int main()
{
  float a = 37.10;
  float b = 28.75;
  float res = a - b;

  fesetround(FE_TONEAREST);

  printf("%.2f\n", res);
}

Solution 2:[2]

Binary floating point is, after all, binary, and if you do care about the correct decimal rounding this much, then your choices would be:

  • decimal floating point, or
  • fixed point.

I'd say the solution is to use fixed point, especially if you're on embedded, and forget about everything else.

With

int32_t a = 3710;
int32_t b = 2875;

the result of

a - b

will exactly be

835

every time; and then you just need to have a simple fixed point printing routine for the desired precision, and check the following digit after the last digit to see if it needs to be rounded up.

Solution 3:[3]

If you want to round to 2 decimals, you can add 0.005 to the result and then offset it with floorf:

float f = 37.10f - 28.75f;
float r = floorf((f + 0.005f) * 100.f) / 100.f;

printf("%f\n", r);

The output is 8.350000

Why are you using floats instead of doubles?

Regarding your question:

Is it valid to add following to the result:

        float result = a - b;

        if (result > 0.0f)
        {
            result += powf(10, -nr_of_decimals - 1) / 2;
        }
        else
        {
            result -= powf(10, -nr_of_decimals - 1) / 2;
        }

It doesn't seem so, on my computer I get 8.350498 instead of 8.350000.

After your edit:

Calculation 37.1-28.75 will result into floating point 8.349998, which will result printf rounding to 8.3 instead of 8.4.

Then

float r = roundf((f + (f < 0.f ? -0.05f : +0.05f)) * 10.f) / 10.f;

is what you are looking for.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Peter
Solution 2 Peter O.
Solution 3