-1

I wish to round a float value, say val to its nearest multiple of 0.05. An explanation of my intent is here. I already have upper and lower bounds of val, say valU and valL respectively. I can do this in the following ways:

  1. Search for the nearest multiple of 0.05 in the range [valL, valU] and assigning the value accordingly. Ties are settled by taking lower value. OR
  2. Using something like this (off-course replacing 20 by 100 in the solution given in link).

I find that method-2 yields wrong results some times. Can someone please tell me why method-1 is right way?

Community
  • 1
  • 1
Abhinav
  • 1,882
  • 1
  • 18
  • 34

1 Answers1

1

... round a float value, say val to its nearest multiple of 0.05 ...

Given typical binary floating point, the best that can be had is

... round a float value to nearest to a multiple of 0.05 R and save as the nearest representable float r.


Method #1 is unclear in the corner cases. What OP has is not code, but an outline of code from a math perspective and not an actual C code one. I'd go with a variation on #2 that works very well. @Barmar

Almost method 2: Multiply, round, then divide.

#include <math.h>
double factor = 20.0;
float val = foo();
double_or_float rounded_val = round(val * factor)/factor;

This method has two subtle points that make it superior.

  1. The multiplication is done with greater precision and range than the referenced answer - this allows for an exact product and a very precise quotient. If the product/quotient were calculated with only float math, some edge cases would end up with the wrong answer and of course some large values would overflow to infinity.

  2. "Ties are settled by taking lower value." is a tough and unusual goal. Sounds like a goal geared to skew selection. round(double) nicely rounds half way cases away from zero regardless of the current rounding direction. To accomplish "lower", change the current rounding direction and use rint() or nearbyint().

    #include <fenv.h>
    int current_rounding_direction = fegetround();
    fesetround(FE_DOWNWARD);
    double rounded_val = rint(val * factor)/factor;
    fesetround(current_rounding_direction);
    

... method-2 yields wrong results some times...

OP needs to post the code and the exact values used and calculated for a quality explanation of the strength/weakness of various methods. Try printf("%a %a\n", val, rounded_val);. Often, problems occurs due to imprecise understanding of the exact values used should code use printf("%f\n", val);


Further: "I already have upper and lower bounds of val, say valU and valL respectively. I can do this in the following ways:"

This is doubtfully accurate as the deviation of valU and valL is just an iteration of the original problem - to find rounded_val. The code to find valL and valU each needs an upper and lower bound, else what is to prevent range [valL ... valU] from itself having inaccurate endpoints?

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256