float rounding issue

So I have a function like this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
float roundToSignificant(float num, int n)
{
	if(num == 0.0f) return 0.0f;
	
	float magnitude = pow(10, (float)n);
	int shifted = round(num*magnitude);

	return (float)(shifted/magnitude);
}

float round(float r)
{
	return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
}

which basically rounds a value to the nth floating point.

It also works fine in most cases, however for example roundToSignificant(900000.05f, 1) returns 900000.13 but it should return 900000.10.

When debugging through the code the issue seems to happen at the very last line of the function so the values there look like this -> return (float)(9000001/10.000000);

I know calculating around with float values isn't exactly accurate but I would like to know if there is any way to solve this issue here to get the desired result?
Last edited on
Hmm let me pull out my handy dandy debugging super tool for this one. I call it, cout :P.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <cmath>
float round(float);
float roundToSignificant(float num, int n)
{
	if(num == 0.0f) return 0.0f;

	float magnitude = pow(10, (float)n);
	std::cout<<"1:"<<pow(10, (float)n)<<"\n";//10
	int shifted = round(num*magnitude);
    std::cout<<"3:"<<(float)(shifted/magnitude)<<"\n";//1
	return (float)(shifted/magnitude);
}

float round(float r)
{
    std::cout<<"2:"<<(r > 0.0)?floor(r + 0.5):ceil(r - 0.5);
    std::cout<<"\n";
	return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);

}

int main()
{
    std::cout<<"final:"<<roundToSignificant(900000.05,1);
    return 0;
}

output:
1:10
2:1
3:900,000
final:900,000


You might have to work with me here because I'm kind of lost on how you got your numbers.
> returns 900000.13 but it should return 900000.10.

900000.10 may not have a floating point representation at all; what you would get is the nearest representable floating point value.

1
2
3
4
5
6
7
8
#include <iostream>

int main()
{
    const float seq[] = { 900000.000f, 900000.100f, 900000.200f, 900000.300f, 900000.400f }  ;

    for( float f : seq ) std::cout << std::fixed << f << '\n' ;
}

http://coliru.stacked-crooked.com/a/c836cb891ef855c6
> You might have to work with me here because I'm kind of lost on how you got your numbers.

Why, do you get different values? It could be because you are using a different debugger/platform/compiler (Windows 7, VS2010).

> 900000.10 may not have a floating point representation at all; what you would get is the nearest representable floating point value.

So it might not be an inaccurate value afterall, it just doesn't get more precise? I would actually be okay with that as long as other values in the area near that value get the same value assigned (for storing purposes).
> So it might not be an inaccurate value afterall, it just doesn't get more precise?

Yes. It doesn't get more precise than half a ulp.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <boost/math/special_functions/next.hpp>

int main()
{
    const float lb = 900000.0 ;
    const float ub = boost::math::float_next( lb + 1.0 ) ;

    std::cout << "number of ulps between " << lb << " and " << ub << " is "
               << boost::math::float_distance( lb, ub ) << "\n\n" ;
    int n = 0 ;
    for( float f = lb ; f < ub ; f = boost::math::float_next(f) )
        std::cout << std::setw(2) << ++n << ". " << std::fixed << f << '\n' ;
}


On my implementation:
number of ulps between 900000 and 900001 is 16

 1. 900000.000000
 2. 900000.062500
 3. 900000.125000
 4. 900000.187500
 5. 900000.250000
 6. 900000.312500
 7. 900000.375000
 8. 900000.437500
 9. 900000.500000
10. 900000.562500
11. 900000.625000
12. 900000.687500
13. 900000.750000
14. 900000.812500
15. 900000.875000
16. 900000.937500



> I would actually be okay with that as long as other values in the area
> near that value get the same value assigned

The IEEE 754 specification—followed by all modern floating-point hardware—requires that the result of an elementary arithmetic operation (addition, subtraction, multiplication, division, and square root since 1985, and FMA since 2008) be within 0.5 ULP of the mathematically exact result, using John Harrison's definition—that is, that it be the best possible result.
- http://en.wikipedia.org/wiki/Unit_in_the_last_place


You might want to read this: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Last edited on
Thanks for the explanation and links
Topic archived. No new replies allowed.