-2

I have seen this code:

(int)(num < 0 ? (num - 0.5) : (num + 0.5))

(How to round floating point numbers to the nearest integer in C?) for rounding but I need to use float and precision for three digits after the point. Examples: 254.450 should be rounded up to 255. 254.432 should be rounded down to 254 254.448 should be rounded down to 254 and so on.

Notice: This is what I mean by "3 digits" the bold digits after the dot.

I believe it should be faster then roundf() because I use many hundreds of thousands rounds when I need to calculate the rounds. Do you have some tips how to do that? I tried to search source of roundf but nothing found.

Note: I need it for RGB2HSV conversion function so I think 3 digits should be enough. I use positive numbers.

Community
  • 1
  • 1
John Boe
  • 3,501
  • 10
  • 37
  • 71
  • 1
    You are asking for "three digits after the point" but all three examples that you provide round down to zero digits after the point. What the? – Mike Nakis Feb 07 '15 at 16:15
  • 4
    I think you misunderstand rounding. Why would *254.450* round up to *255*? – Uyghur Lives Matter Feb 07 '15 at 16:19
  • Round 0.45 to 0.5 and 0.5 to 1 so 254 + 1 is 255 – John Boe Feb 07 '15 at 16:25
  • just change the 0.5 in your formula to something else to adjust the rounding cut off point – thang Feb 07 '15 at 16:34
  • `254.450` is typically not exact representable as a `float`. The closest `float` is `254.4499969482421875000...` which rounds per your method to `254.4` --> `254.0`. – chux - Reinstate Monica Feb 07 '15 at 16:36
  • @user1141649: With your logic, 254.448 would be rounded to 255 as well (because 0.448 is rounded to 0.45, 0.45 rounded to 0.5, and 0.5 rounded to 1.0). But that is not the way that rounding is usually done. 254.450 is closer to 254 than to 255, therefore round(254.450) = 254 is a correct result. – Martin R Feb 07 '15 at 16:41
  • "Round 0.45 to 0.5 and 0.5 to 1 so 254 + 1 is 255" is not a standard form of mathematical rounding. The tpyical rounded valueof `254.45` is `254.0`. See [Double rounding](http://en.wikipedia.org/wiki/Rounding#Double_rounding). – chux - Reinstate Monica Feb 07 '15 at 16:42

5 Answers5

3

"it should be faster then roundf()" is only verifiable with profiling various approaches.

To round to 0 places (round to nearest whole number), use roundf()

float f;
float f_rounded3 = roundf(f);

To round to 3 places using float, use round()

The round functions round their argument to the nearest integer value in floating-point format, rounding halfway cases away from zero, regardless of the current rounding direction.

#include <math.h>

float f;
float f_rounded3 = round(f * 1000.0)/1000.0;

Code purposely uses the intermediate type of double, else code code use with reduced range:

float f_rounded3 = roundf(f * 1000.0f)/1000.0f;

If code is having trouble rounding 254.450 to 255.0 using roundf() or various tests, it is likely because the value is not 254.450, but a float close to it like 254.4499969 which rounds to 254. Typical FP using a binary format and 254.450 is not exactly representable.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • `roundf(254.450) = 254` is a *correct* result (and would not suffer from precision problems). I don't understand why OP wants that rounded to 255. – Martin R Feb 07 '15 at 16:35
  • @Martin R OP appears to round in steps and incur the problems of [successive rounding](http://en.wikipedia.org/wiki/Rounding#Double_rounding). Best to use `roundf()`. That is why it is in the library. – chux - Reinstate Monica Feb 07 '15 at 16:40
  • So roundf does not round as I expected. If you have number 0.449 so first you should round the last digit which is: 0.450, then you should round the second digit: 0.500, then you should round the to 1... This is what I want – John Boe Feb 07 '15 at 16:42
  • 1
    @user1141649 True that `roundf()` is not rounding as you expect. But `roundf()` is rounding the way the vast majority of folks expect. Your method, though maybe valid for your purpose, is contrary to mathematics and computer science. It appears you want any fraction value 0.4445000 (or maybe 0.445000) or higher to round up, else down. In that case add +/- (0.5000-0.4445) to your value and proceed with `roundf()`. – chux - Reinstate Monica Feb 07 '15 at 16:50
  • In php there exist such function which can round with precision: `round($num,$precision);` I was used to use it often, but unfortunately I don't know its source code. – John Boe Feb 07 '15 at 19:17
  • 1
    @user1141649 The PHP `round()` using default rounding, would perform like this answer, certainly not rounding as you propose. Implementations vary but the equivalent functionality is to scale (* 1000 for 3 places) the number, round to nearest whole number (ties direction depended on mode), then scale back (/1000 for 3 places.) – chux - Reinstate Monica Feb 07 '15 at 22:54
1

When you can change de border from b=0.5 to b=0.45 you must know that for positives the rounded value is round_0(x,b)=(int)( x+(1-b) ) therefore b=0.45 ⟹ round_0(x)=(int)(x+0.55) and you can threat the signal. But remember that don't exists 254.45 but 254.449997 and 254.449999999999989, maybe you prefer to use b=0.4495.

If you have float round_0(float) to zero-digit rounding (can be like you show in question), you can do for one, two... n-digit rounding like this in C/C++: # define round_n(x,n) (round_0((x)*1e##n)/1e##n).

round_1( x , b ) = round_0( 10*x ,b)/10

round_2( x , b ) = round_0( 100*x ,b)/100

round_3( x , b ) = round_0( 1000*x ,b)/1000

round_n( x , b , n ) = round_0( (10^n)*x ,b)/(10^n)

But do typecast to int and (one more typecast) to float to operate is slower than rounds in operations. If don't simplify the add/sub (some compilers have this setting) for faster zero-digit round to float type you can do it.

inline float round_0( float x , float b=0.5f ){
    return (( x+(0.5f-b) )+(3<<22))-(3<<22) ;  // or (( x+(0.5f-b) )-(3<<22))+(3<<22) ;
}

inline double round_0( double x , double b=0.5 ){
    return (( x+(0.5-b) )+(3<<51))-(3<<51) ;  // or (( x+(0.5-b) )-(3<<51))+(3<<51) ;
}

When b=0.5 it correctly rounds to nearest integer if |x|<=2^23 (float) or |x|<=2^52 (double). But if compiler uses FPU (ten bytes floating-point) optimizing loads then constant is 3.0*(1u<<63), works |x|<=2^64 and use long double can be faster.

RHER WOLF
  • 137
  • 10
0

You can use double transformation float -> string -> float, while first transformation make 3 digits after point:

  sprintf(tmpStr, "%.3f", num);
VolAnd
  • 6,367
  • 3
  • 25
  • 43
0

this work for me

#include <stdio.h>


int main(int ac, char**av)                                                                    
{                                                                                             
  float val = 254.449f;                                                                       
  float val2 = 254.450f;                                                                      
  int res = (int)(val < 0 ? (val - 0.55f) : (val + 0.55f));                                   
  int res2 = (int)(val2 < 0 ? (val2 - 0.55f) : (val2 + 0.55f));                               
  printf("%f %d %d\n", val, res, res2);                                                       
  return 0;                                                                                   
}                                                                                             

output : 254.449005 254 255

to increase the precision just add any 5 you want in 0.55f like 0.555f, 0.5555f, etc

  • I tested your code but my result is `254.449005 254 254` – John Boe Feb 07 '15 at 16:28
  • It's because the floating points are hard to implement as you can see the val is not exactly 254.449f so you can test with 0.56f but with a lot of precision nothing will work exactly whatever you want. – Gabrielle de Grimouard Feb 07 '15 at 16:33
0

I wanted something like this:

float num = 254.454300;
float precision=10;
float p = 10*precision;
num = (int)(num  * p + 0.5) / p ;

But the result will be inaccurate (with error) - my x86 machine gives me this result: 254.449997

John Boe
  • 3,501
  • 10
  • 37
  • 71