Java Math.pow issue - java

I am working on a program which needs to cube a number. Long story short, I need to compare the number to a string, so I need to get rid of the decimal place a double gives when I convert to string. To do this, I used Math.round and saved it as a long. This works fine for relatively normal numbers, but the numbers can go up to 999,999.
I used 275393 (a given test number, so I'm assuming it must be correct for the problem I'm working on) and neither a calculator nor the computer seemed to get the correct answer. The correct answer is supposed to contain 123457 somewhere in the results, but the calculator has 12346 (which I think is just rounding, as it stops listing numbers after this) and the computer has 123456 (the computer stops listing numbers after this point). Is rounding it giving it the problem (it shouldn't because I'm pretty sure it only rounds to the tenths place, but who knows)? Or is it something else?

Math.pow() takes two doubles and returns a double. There is not enough precision in a double to represent 2753933 = 20886164356123457 (exact).
The solution is to use BigInteger.pow().

A double has limited precision. Instead, use BigInteger or BigDecimal for your calculation.

I need to compare the number to a string, so I need to get rid of the decimal place a double gives when I convert to string.
Do it the other way around. Convert the String to a number, then compare it to the double (with a small tolerance to account for inaccurate binary representations of float point numbers).

Related

How to round doubles to a specified decimal place without Libraries?

Basically I'm supposed to write a method, that uses a double "x" and an int "y" and rounds (>=5 upwards, <5 downwards) "x" to the decimal place specified by y (between 1-8). The method is supposed to return a double. However since I just started I don't have a clue how to achieve this. The exercise prior to this one way easier.
If read answers to similar question but they are not quit what I need, because I can't use the Math library or other libraries. I'm allowed to make auxiliary methods to substitute this.
Rounding like that with double isn't going to work. Doubles and floats are represented with a fixed number of bits of data, and work in binary not decimal. That means that some numbers can't be represented. .1 can't be stored exactly.
In order to do this, you need to do use BigDecimal, which is a class that can store any exact number. Math using BigDecimal is less efficient, but it doesn't have the accuracy issues of doubles.

The accuracy of a double in general programming and Java

I understand that due to the nature of a float/double one should not use them for precision important calculations. However, i'm a little confused on their limitations due to mixed answers on similar questions, whether or not floats and doubles will always be inaccurate regardless of significant digits or are only inaccurate up to the 16th digit.
I've ran a few examples in Java,
System.out.println(Double.parseDouble("999999.9999999999");
// this outputs correctly w/ 16 digits
System.out.println(Double.parseDouble("9.99999999999999");
// This also outputs correctly w/ 15 digits
System.out.println(Double.parseDouble("9.999999999999999");
// But this doesn't output correctly w/ 16 digits. Outputs 9.999999999999998
I can't find the link to another answer that stated that values like 1.98 and 2.02 would round down to 2.0 and therefore create inaccuracies but testing shows that the values are printed correctly. So my first question is whether or not floating/double values will always be inaccurate or is there a lower limit where you can be assured of precision.
My second question is in regards to using BigDecimal. I know that I should be using BigDecimal for precision important calculations. Therefore I should be using BigDecimal's methods for arithmetic and comparing. However, BigDecimal also includes a doubleValue() method which will convert the BigDecimal to a double. Would it be safe for me to do a comparison between double values that I know for sure have less than 16 digits? There will be no arithmetic done on them at all so the inherent values should not have changed.
For example, is it safe for me to do the following?
BigDecimal myDecimal = new BigDecimal("123.456");
BigDecimal myDecimal2 = new BigDecimal("234.567");
if (myDecimal.doubleValue() < myDecimal2.doubleValue()) System.out.println("myDecimal is smaller than myDecimal2");
Edit: After reading some of the responses to my own answer i've realized my understanding was incorrect and have deleted it. Here are some snippets from it that might help in the future.
"A double cannot hold 0.1 precisely. The closest representable value to 0.1 is 0.1000000000000000055511151231257827021181583404541015625. Java Double.toString only prints enough digits to uniquely identify the double, not the exact value." - Patricia Shanahan
Sources:
https://stackoverflow.com/a/5749978 - States that a double can hold up to 15 digits
I suggest you read this page:
https://en.wikipedia.org/wiki/Double-precision_floating-point_format
Once you've read and understood it, and perhaps converted several examples to their binary representations in the 64 bit floating point format, then you'll have a much better idea of what significant digits a Double can hold.
As a side note, (perhaps trivial) a nice and reliable way to store a known precision of value is to simply multiply it by the relevant factor and store as some integral type, which are completely precise.
For example:
double costInPounds = <something>; //e.g. 3.587
int costInPence = (int)(costInPounds * 100 + 0.5); //359
Plainly some precision can be lost, but if a required/desired precision is known, this can save a lot of bother with floating point values, and once this has been done, no precision can be lost by further manipulations.
The + 0.5 is to ensure that rounding works as expected. (int) takes the 'floor' of the provided double value, so adding 0.5 makes it round up and down as expected.

Wrong Decimal Converting from Double to String in java

I have A String that is formatted correctly to be cast to a double and it works fine for most decimals. The issue is that for .33, .67, and possibly others I haven't tested, the decimal becomes something like .6700000000002, or .329999999998. I understand why this happens but does any one have a suggestion to fix it.
It's a result of IEEE-754 rounding rules, some numbers cannot be represented precisely in two's complement. For example, 1/10 is not precisely representable.
You can add more precision (but not infinite) by using BigDecimal.
BigDecimal oneTenth = new BigDecimal("1").divide(new BigDecimal("10"));
System.out.println(oneTenth);
Which outputs 0.1
Some decimal numbers can not be represented accurately with the internal base 2 machine representation.
That's double precision for you. Binary numbers and decimals don't work well together. Unless you are doing something really precise it should be fine, if you are printing it you should use either decimal format or printf.
Value of floating point numbers are not stored directly but with exponential values. You may write 3.1233453456356 as number, but this is stored something like 3 and 2^6 in memory. It tries to store a value as close as your number, but those differences can happen.
It shouldn't be a problem unless you're testing for equality. With floating-point tests for equality you'll need to allow a "delta" so that:
if (a == b)
becomes
if (abs(a-b) < 0.000001)
or a similar small delta value. For printing, limit it to two decimal places and the formatter will round it for you.

Is there a rounding algorithm that undoes base 2 conversion and infers precision? *hopefully in java*

If I have a number like 3.01 the computer seems to think the best double is the 64 bit number:
3.0099999999999997868371792719699442386627197265625
Is there some way better than looking for say more than four 9's or 0's that I can generically "round" to the precise base 10 representation?
Is there an algo that would take that 3.00999999... mess and return 3.01 WITHOUT me specifying that I want that precision.
I think most of the numbers I'm dealing with should be small enough that 64-bits will not have ambiguities.
No - because presumably you might have actually specified 3.0099999999999997868 as the input number, and wouldn't want that same value to be rounded to 3.01. Basically, you've lost information when converting from a decimal value to binary floating point - you can't get that information back.
If you're interested in decimal values rather than just the magnitude, you should consider using BigDecimal instead of double. (What do these values represent?)
EDIT: As noted by other answers, Java will give you 3.01 anyway when you just use toString, however you came to the original value. This is specified in Double.toString:
How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double. That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument d. Then d must be the double value nearest to x; or if two double values are equally close to x, then d must be one of them and the least significant bit of the significand of d must be 0.
If that's good enough for you, it'll make life easier... but it sounds like you should be thinking about it more fundamentally.
If you want 10 digits of precision, you need to round to that precision. Even if you use BigDecimal you can avoid representation error, but sooner or later you will have to know how to deal with precision.
double d = 3.01;
System.out.println(d); // rounds the answer slightly
prints
3.01
There are many workarounds for representation and rounding error, however often the built in tools will deal with it for you.
It's clear that you cannot expect to always get the original number back since there are many numbers that map to the same float. For example, you cannot distinguish between these numbers:
3.0099999999999997868371792719699442386627197265625
3.009999999999999786837179271969944238662
3.009999999999999786837179271
3.0099999999999997
3.01
However, Python has an interesting take on this: if you give it the number 3.0099999999999997868371792719699442386627197265625, it will reply with 3.01:
Python 2.7.2+ (default, Nov 30 2011, 19:22:03)
[GCC 4.6.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 3.0099999999999997868371792719699442386627197265625
3.01
This is becauuse 3.01 is the shortest string that gives the same floating point number. In other words, it's the shortest x so that
float(repr(x)) == x
where repr is the Python function that turns an object into a string (here it turns 3.0099... into 3.01) and float converts a string to a float.
There are obvisouly many strings that will result in the same internal float, but this is the shortest and therefore "probably" what you meant.
This feature was added in Python 2.7, as a backport of a Python 3.1 feature. It was discussed in Issue1580 and you should be able to find the code there and translate it into Java if you want.

Four questions - Calculator Application - Android Java

Hey all, I am a total newbie developing an android application, I've been reading 'Sams Teach Yourself Java in 24 hours' and it's a great book. But I have been stuck on a bit where I get the value of a decimal number only editTexts and use java maths to work out my end value.
Is there a way to have an editText input straight to a float or double variable rather than to a string and then from a string to a double?
Are there any real issues with converting between a string and a double or float or will the values remain the same and not be polluted.
Differences / pros and cons of using a doble as opposed to a float.
Best way to input a fraction value from the user?
Thanks for any help. Have a good day.
No, you can't.
Yes. If your string is, say, an ID and reads like "0029482", after you turn it into an integer it will read "29482" and probably will be invalid. Strings can be longer than doubles or floats, and if you have a value like "0.12345678901234567890123456789" in a string, you will lose a lot of precision by converting that to a double.
Doubles use double the number of bits (hence the name), and can therefore hold more precision.
Accept the denominator and numerator integers, and store them in a custom class.
No. You could write your own subclass that makes it seem like that is what's happening, but at some point somewhere in the chain you have to do a conversion from character/text data to numerical data.
Yes. Primitive floating-point types use IEEE-754 to encode decimal numbers in binary. The encoding provides very good precision, but it is not exact/cannot exactly represent many possible numbers. So if you parse from a string to a primitive floating-point type, and then back to string again, you may get something that is different from your input string.
A double uses twice as many bits to encode the number as a float, and thus is able to maintain a greater degree of precision. It will not, however, remove the issues discussed in #2. If you want to remove those issues, consider using something like BigDecimal to represent your numbers instead of primitive types like float or double.
Read the whole thing as a string, split() it on the '/' character, and then store each part as an integer (or BigInteger). If you need to display it as a decimal, use BigDecimal to perform the division.
I'd just like to add that if you are looking for an alternative to double or float that doesn't entail loss of precision when converting between strings and numeric form, look at these:
The standard java.math.BigDecimal class represents an arbitrary precision scaled number; i.e. an arbitrary precision integer multiplied (scaled) by a fixed integer power of 10.
The Apache dfp package contains implementations of decimal-based floating numbers.
However, I'd steer clear of both of this topic for now, and implement using float or double. (I take it that your real aim is to learn how to write Java, not to build the world's greatest calculator app.)

Categories