Bick
Bick

Reputation: 18521

java - remove float leftovers elegantly

In brief - I am having a hard time with the float left overs
(i.e. 10.00000123 instead of 10)

Here I go :

I need to generate list of floats with constant gap as follows (actually its a map , I need to retreive the object nut nevermind that)

List A: 0.25, 0.5, 0.75, 1, ...
or
List B: 0.01, 0.02, 0.03, 0.04, ...

every time I get a number and I round it to the neerest cell in the list.
lets say I get 0.051 to retreive a cell in list A - I return 0.05.
lets say I get 0.21 to retreive a cell in list B - I return 0.25.

So I started be doing this

float a = Math.round(Value / step) * step;

but than I get a lot of time 0.2500001 (float leftovers ) I need a smart way to round it .

Maybe by taking the number of digits after the dot and doing again

Math.round(Value / 100) * 100;?

Is there a smarter way? I tried doig this

        final float factor = Math.round(1 / step);
        final float value = (float) Math.round(value * factor) / factor;

but I sometimes have a list like this

List A: 10, 15 , 20, 25, 30, ...

and when I get 22 I retreive the cell of 20. the problem is that When I get a gap of 10

Math.round(1 / baseAssetStep) 

returns 0 - and I get NaN

Upvotes: 3

Views: 2822

Answers (4)

Peter Lawrey
Peter Lawrey

Reputation: 533520

Firstly, I would use double or long instead as these have much more digits of accuracy. If you really need to, use BigDecimal, but its pretty rare to find a real world situation where double or long would not do the job.

double d = 10.00000123;
double r = Math.round(d * 10000) / 10000.0;

or using long with fixed point precision.

long l = 100000; // the actual value * 10000

A common use case for fixed point precision is money. Instead of using dollars with double use cents with long or even int instead.

Upvotes: 2

yshavit
yshavit

Reputation: 43391

In short, "these are not the numbers you're looking for."

Floating points are represented as binary fractional numbers, and some numbers that can be easily represented in base-10 (0.01, for instance) can't be represented with a finite number of binary digits. This is similar to how 1/3 is easy in base 3 (it's just 0.1), but requires an infinite number of digits in base 10 (0.333...).

If you tried to represent 1/3 with a finite number of digits in base 10, you'd get an approximation. Similarly, if you try to represent 1/10 with a finite number of digits in base 2 (which is what float and double do), you'll get an approximation, and similarly with 1/100. What you think is 0.01 in the code is actually a number that's very close, but not exactly equal to, 1/100.

There are many resources out there concerning floating points and the difficulty in working with them. http://floating-point-gui.de/ is a good place to start.

Upvotes: 1

PiotrkS
PiotrkS

Reputation: 381

you need a constant gap, so maybe try another solution. Take your gap, let's name it G. Draw a randow int - let's call it R, (if you know maximum number in your list you can draw it properly). Now the only thing to do would be R*G which will give you a number from your list - of course with some precision because this is inevitable using float - to print it just use format. You can also combine this with BigDecimal ;)

Upvotes: 0

Peter Lang
Peter Lang

Reputation: 55524

Use BigDecimal instead of float.


From the Java Tutorials of Primitive Data Types:

float: [...] This data type should never be used for precise values, such as currency. For that, you will need to use the java.math.BigDecimal class instead. Numbers and Strings covers BigDecimal and other useful classes provided by the Java platform.

Upvotes: 6

Related Questions