Lost precision. How should I handle this? - java

I am modelling a system which contains a specific amount of energy between 0 and 1. This is stored in a double called storedEnergy.
storedEnergy should count from 0.0 to 1.0 over 21 minutes, but instead counts to 1.0000000000000004
Every iteration storedEnergy increases by onStoredEnergyIncreasePerMinute which is calculated like this:
onSlackMinutesIncreasePerMinute = (double) time1to0Energy / (double) time0to1Energy;
onStoredEnergyIncreasePerMinute = 1d / (double) time0to1Energy;
offSlackMinutesDecreasePerMinute = 1d;
offStoredEnergyDecreasePerMinute = 1d / (double) time1to0Energy;
Here is the stored energy increasing:
while (storedEnergy < target) {
storedEnergy += onStoredEnergyIncreasePerMinute;
}
A similar thing happens with slack minutes: there are 24.999999999999993 at 1.0 energy, when there should be 25 at 1.0
It may be relevant to mention that I will then count back down to 0 using offStoredEnergyDecreasePerMinute for 25 minutes
I don't know why this is happening (though I presume it's something to do with doubles not being able to represent fractions properly), or what I should do to resolve it. Do I have to use some sort of fraction class?

I presume it's something to do with doubles not being able to represent fractions properly
That's right, floating-point numbers can only represent binary fractions exactly, and up to a certain limit of precision.
You should carefully consider what set of numbers your computation uses. If it's just rational numbers, then you can relatively simply implement that with two integers. If you take square roots or similar, then no numerical representation will be exact.

Looks like your over adding here:
while (storedEnergy < target) {
//this goes above 1.0 on the final iteration
storedEnergy += onStoredEnergyIncreasePerMinute;
}
you can fix by doing:
while (storedEnergy < target) {
storedEnergy += onStoredEnergyIncreasePerMinute;
if(storedEnergy>target)
storedEnergy = target;
}

Read What Every Computer Scientist Should Know About Floating-Point Arithmetic to understand why these errors occur. Try setting the precision you want your variables to have or use rounding functions, to bypass the representation problem

Related

int or float to represent numbers that can be only integer or "#.5"

Situation
I am in a situation where I will have a lot of numbers around about 0 - 15. The vast majority are whole numbers, but very few will have decimal values. All of the ones with decimal value will be "#.5", so 1.5, 2.5, 3.5, etc. but never 1.1, 3.67, etc.
I'm torn between using float and int (with the value multiplied by 2 so the decimal is gone) to store these numbers.
Question
Because every value will be .5, can I safely use float without worrying about the wierdness that comes along with floating point numbers? Or do I need to use int? If I do use int, can every smallish number be divided by 2 to safely give the absolute correct float?
Is there a better way I am missing?
Other info
I'm not considering double because I don't need that kind of precision or range.
I'm storing these in a wrapper class, if I go with int whenever I need to get the value I am going to be returning the int cast as a float divided by 2.
What I went with in the end
float seems to be the way to go.
This is not a theoretical proof but you can test it empirically:
public static void main(String[] args) {
BigDecimal half = new BigDecimal("0.5");
for (int i = 0; i < Integer.MAX_VALUE; i++) {
float f = i + 0.5f;
if (new BigDecimal(f).compareTo(new BigDecimal(i).add(half)) != 0) {
System.out.println(new BigDecimal(i).add(half) + " => " + new BigDecimal(f));
break;
}
}
}
prints:
8388608.5 => 8388608
Meaning that all xxx.5 can be exactly represented as a float between 0.5 and 8388607.5.
For larger numbers float's precision is not enough to represent the number and it is rounded to something else.
Let's refer to the subset of floating point numbers which have a decimal portion of .0 or .5 as point-five floats, or PFFs.
The following properties are guaranteed:
Any number up to 8 million or so (2^23, to be exact) which ends in .0 or .5 is representable as a PFF.
Adding/subtracting two PFFs results in a PFF, unless there's overflow.
Multiplying a PFF by an integer results in a PFF, unless there's overflow.
These properties are guaranteed by the IEEE-754 rules, which give a 24-bit mantissa and guarantee exact rounding of exact results.
Using ints will give you a somewhat larger range.
There will be no accuracy issues with .5's with float for that range, so both approaches will work.
If these represent actual number values, I would chose the float simply because it consumes the same amount of memory and I don't need to write code to convert between some internal int representation and the exposed float value.
If these numbers represent something other than a value, e.g. a grade from a very limited set, I would consider modelling them as an enum, depending on how these are ultimately used.

Java: convert float to double preserving decimal point precision

I have a float-based storage of decimal by their nature numbers. The precision of float is fine for my needs. Now I want is to perform some more precise calculations with these numbers using double.
An example:
float f = 0.1f;
double d = f; //d = 0.10000000149011612d
// but I want some code that will convert 0.1f to 0.1d;
Update 1:
I know very well that 0.1f != 0.1d. This question is not about precise decimal calculations. Sadly, the question was downvoted. I will try to explain it again...
Let's say I work with an API that returns float numbers for decimal MSFT stock prices. Believe or not, this API exists:
interface Stock {
float[] getDayPrices();
int[] getDayVolumesInHundreds();
}
It is known that the price of a MSFT share is a decimal number with no more than 5 digits, e.g. 31.455, 50.12, 45.888. Obviously the API does not work with BigDecimal because it would be a big overhead for the purpose to just pass the price.
Let's also say I want to calculate a weighted average of these prices with double precision:
float[] prices = msft.getDayPrices();
int[] volumes = msft.getDayVolumesInHundreds();
double priceVolumeSum = 0.0;
long volumeSum = 0;
for (int i = 0; i < prices.length; i++) {
double doublePrice = decimalFloatToDouble(prices[i]);
priceVolumeSum += doublePrice * volumes[i];
volumeSum += volumes[i];
}
System.out.println(priceVolumeSum / volumeSum);
I need a performant implemetation of decimalFloatToDouble.
Now I use the following code, but I need a something more clever:
double decimalFloatToDouble(float f) {
return Double.parseDouble(Float.toString(f));
}
EDIT: this answer corresponds to the question as initially phrased.
When you convert 0.1f to double, you obtain the same number, the imprecise representation of the rational 1/10 (which cannot be represented in binary at any precision) in single-precision. The only thing that changes is the behavior of the printing function. The digits that you see, 0.10000000149011612, were already there in the float variable f. They simply were not printed because these digits aren't printed when printing a float.
Ignore these digits and compute with double as you wish. The problem is not in the conversion, it is in the printing function.
As I understand you, you know that the float is within one float-ulp of an integer number of hundredths, and you know that you're well inside the range where no two integer numbers of hundredths map to the same float. So the information isn't gone at all; you just need to figure out which integer you had.
To get two decimal places, you can multiply by 100, rint/Math.round the result, and multiply by 0.01 to get a close-by double as you wanted. (To get the closest, divide by 100.0 instead.) But I suspect you knew this already and are looking for something that goes a little faster. Try ((9007199254740992 + 100.0 * x) - 9007199254740992) * 0.01 and don't mess with the parentheses. Maybe strictfp that hack for good measure.
You said five significant figures, and apparently your question isn't limited to MSFT share prices. Up until doubles can't represent powers of 10 exactly, this isn't too bad. (And maybe this works beyond that threshold too.) The exponent field of a float narrows down the needed power of ten down to two things, and there are 256 possibilities. (Except in the case of subnormals.) Getting the right power of ten just needs a conditional, and the rounding trick is straightforward enough.
All of this is all going to be a mess, and I'd recommend you stick with the toString approach for all the weird cases.
If your goal is to have a double whose canonical representation will match the canonical representation of a float converting the float to string and converting the result back to double would probably be the most accurate way of achieving that result, at least when it's possible (I don't know for certain whether Java's double-to-string logic would guarantee that there won't be a pair of consecutive double values which report themselves as just above and just-below a number with five significant figures).
If your goal is to round to five significant figures a value which is known to have been rounded to five significant figures while in float form, I would suggest that the simplest approach is probably to simply round to five significant figures. If your magnitude of your numbers will be roughly within the range 1E+/-12, start by finding the smallest power of ten which is smaller than your number, multiply that by 100,000, multiply your number by that, round to the nearest unit, and divide by that power of ten. Because division is often much slower than multiplication, if performance is critical, you might keep a table with powers of ten and their reciprocals. To avoid the possibility of rounding errors, your table should store for each power of then the closest power-of-two double to its reciprocal, and then the closest double to the difference between the first double and the actual reciprocal. Thus, the reciprocal of 100 would be stored as 0.0078125 + 0.0021875; the value n/100 would be computed as n*0.0078125 + n*0.0021875. The first term would never have any round-off error (multiplying by a power of two), and the second value would have precision beyond that needed for the final result, so the final result should thus be rounded accurately.

Java's '==' operator on doubles

This method returns 'true'. Why ?
public static boolean f() {
double val = Double.MAX_VALUE/10;
double save = val;
for (int i = 1; i < 1000; i++) {
val -= i;
}
return (val == save);
}
You're subtracting quite a small value (less than 1000) from a huge value. The small value is so much smaller than the large value that the closest representable value to the theoretical result is still the original value.
Basically it's a result of the way floating point numbers work.
Imagine we had some decimal floating point type (just for simplicity) which only stored 5 significant digits in the mantissa, and an exponent in the range 0 to 1000.
Your example is like writing 10999 - 1000... think about what the result of that would be, when rounded to 5 significant digits. Yes, the exact result is 99999.....9000 (with 999 digits) but if you can only represent values with 5 significant digits, the closest result is 10999 again.
When you set val to Double.MAX_VALUE/10, it is set to a value approximately equal to 1.7976931348623158 * 10^307. substracting values like 1000 from that would required a precision on the double representation that is not possible, so it basically leaves val unchanged.
Depending on your needs, you may use BigDecimal instead of double.
Double.MAX_VALUE is so big that the JVM does not tell the difference between it and Double.MAX_VALUE-1000
if you subtract a number fewer than "1.9958403095347198E292" from Double.MAV_VALUE the result is still Double.MAX_VALUE.
System.out.println(
new BigDecimal(Double.MAX_VALUE).equals( new BigDecimal(
Double.MAX_VALUE - 2.E291) )
);
System.out.println(
new BigDecimal(Double.MAX_VALUE).equals( new BigDecimal(
Double.MAX_VALUE - 2.E292) )
);
Ouptup:
true
false
A double does not have enough precision to perform the calculation you are attempting. So the result is the same as the initial value.
It is nothing to do with the == operator.
val is a big number and when subtracting 1 (or even 1000) from it, the result cannot be expressed properly as a double value. The representation of this number x and x-1 is the same, because double only has a limited number of bits to represent an unlimited number of numbers.
Double.MAX_VALUE is a huge number compared to 1 or 1000. Double.MAX_VALUE-1 is generally equals to Double.MAX_VALUE. So your code roughly does nothing when substracting 1 or 1000 to Double.MAX_VALUE/10.
Always remember that:
doubles or floats are just approximations of real numbers, they are just rationals not equally distributed among the reals
you should use very carefully arithmetic operators between doubles or floats which are not close (there is many other rules such like this...)
in general, never use doubles or float if you need arbitrary precision
Because double is a floating point numeric type, which is a way of approximating numeric values. Floating point representations encode numbers so that we can store numbers much larger or smaller than we normally could. However, not all numbers can be represented in the given space, so multiple numbers get rounded to the same floating point value.
As a simplified example, we might want to be able to store values ranging from -1000 to 1000 in some small amount of space where we would normally only be able to store -10 to 10. So we could round all values to the nearest thousand and store them in the small space: -1000 gets encoded as -10, -900 gets encoded as -9, 1000 gets encoded as 10. But what if we want to store -999? The closest value we can encoded is -1000, so we have to encode -999 as the same value as -1000: -10.
In reality, floating point schemes are much more complicated than the example above, but the concept is similar. Floating point representations of numbers can only represent some of all the possible numbers, so when we have a number that can't be represented as part of the scheme, we have to round it to the closest representable value.
In your code, all values within 1000 of Double.MAX_VALUE / 10 automatically get rounded to Double.MAX_VALUE / 10, which is why the computer thinks (Double.MAX_VALUE / 10) - 1000 == Double.MAX_VALUE / 10.
The result of a floating point calculation is the closest representable value to the exact answer. This program:
public class Test {
public static void main(String[] args) throws Exception {
double val = Double.MAX_VALUE/10;
System.out.println(val);
System.out.println(Math.nextAfter(val, 0));
}
}
prints:
1.7976931348623158E307
1.7976931348623155E307
The first of these numbers is your original val. The second is the largest double that is less than it.
When you subtract 1000 from 1.7976931348623158E307, the exact answer is between those two numbers, but very, very much closer to 1.7976931348623158E307 than to 1.7976931348623155E307, so the result will be rounded to 1.7976931348623155E307, leaving val unchanged.

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
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.
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.
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.
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 ;)

Java Double value = 0.01 changes to 0.009999999999999787 [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Why not use Double or Float to represent currency?
I'm writing a basic command-line program in Java for my high school course. We're only working with variables right now. It's used to calculate the amount of bills and coins of whatever type in your change after a purchase. This is my program:
class Assign2c {
public static void main(String[] args) {
double cost = 10.990;
int paid = 20;
double change = paid - cost;
int five, toonie, loonies, quarter, dime, nickel, penny;
five = (int)(change / 5.0);
change -= five * 5.0;
toonie = (int)(change / 2.0);
change -= toonie * 2.0;
loonies = (int)change;
change -= loonies;
quarter = (int)(change / 0.25);
change -= quarter * 0.25;
dime = (int)(change / 0.1);
change -= dime * 0.1;
nickel = (int)(change / 0.05);
change -= nickel * 0.05;
penny = (int)(change * 100);
change -= penny * 0.01;
System.out.println("$5 :" + five);
System.out.println("$2 :" + toonie);
System.out.println("$1 :" + loonies);
System.out.println("$0.25:" + quarter);
System.out.println("$0.10:" + dime);
System.out.println("$0.05:" + nickel);
System.out.println("$0.01:" + penny);
}
}
It should all work but at the last step when there's $0.01 leftover, number of pennies should be 1 but instead, it's 0. After a few minutes of stepping into the code and outputting the change value to the console, I've found out that at the last step when change = 0.01, it changes to 0.009999999999999787. Why is this happening?
Using double for currency is a bad idea, Why not use Double or Float to represent currency?. I recommend using BigDecimal or doing every calculation in cents.
0.01 does not have an exact representation in floating-point (and neither do 0.1 nor 0.2, for that matter).
You should probably do all your maths with integer types, representing the number of pennies.
doubles aren't kept in decimal internally, but in binary. Their storage format is equivalent to something like "100101 multiplied by 10000" (I'm simplifying, but that's the basic idea). Unfortunately, there's no combination of these binary values that works out to exactly decimal 0.01, which is what the other answers mean when they say that floating point numbers aren't 100% accurate, or that 0.01 doesn't have an exact representation in floating point.
There are various ways of dealing with this problem, some more complicated than others. The best solution in your case is probably to use ints everywhere and keep the values in cents.
As the others already said, do not use doubles for financial calculations.
This paper http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html (What Every Computer Scientist Should Know About Floating-Point Arithmetic) is a must-read to understand floating point math in computers.
Floating point numbers are never 100% accurate (not quite true, see comments below). You should never compare them directly. Also integer rounding. The best way to do this would probably be to do it in cents and convert to dollars later (1 dollar == 100 cents). By converting to an integer you are losing precision.
its a float(double)
You should not use it to compute money....
I recommend using int values and operate on pennys
This is a problem that's arisen many times over. The bottom line is that on a computer that uses binary floating point (which Java requires), only fractions in which the denominator is a power of 2 can be represented precisely.
The same problem arises in decimal. 1/3, for example, turns into 0.3333333..., because 3 isn't a factor of 10 (the base we're using in decimal). Likewise 1/17, 1/19, etc.
In binary floating point, the same basic problem arises. The main difference is that in decimal, since 5 is a factor of 10, 1/5 can be represented precisely (and so can multiples of 1/5). Since 5 is not a factor of 2, 1/5 cannot be represented precisely in binary floating point.
Contrary to popular belief, however, some fractions can be represented precisely -- specifically those fractions whose denominators with only 2 as a prime factor (e.g., 1/8 or 1/256 can be represented precisely).
I'm sure you know that some fractions' decimal representations terminate (e.g. .01) while some don't (e.g. 2/3=.66666...). The thing is that which fractions terminate changes depending on what base you're in; in particular, .01 doesn't terminate in binary, so even though double provides a lot of precision it can't represent .01 exactly. As others said, using BigDecimal or fixed-point integer computations (converting everything to cents) is probably best for currency; to learn more about floating point, you could start at The Floating-Point Guide- What Every Programmer Should Know About Floating-Point Arithmetic.

Categories

Resources