Calculation gives different results - java

I'm trying to calculate a formula in Android:
percent = absence/(week*(37/period))*100;
The value for absence is 4, week is 2, and period is 2. percent returns 11.111111.
But if I do the same in Excel with formula 4/(2*(37/2))*100, it returns 10.81.
Can anyone explain me what might be wrong?

It's because of integer division.
The value for absence is 4, week is 2, and period is 2. percent returns 11.111111.
This is because 37 / 2 is 18.5, and the fractional part is thrown out because of integer division. That gives 18, which multiplied by 2 is 36. Next we divide 4 by 36, which is 0.111... That multiplied by 100 is 11.111....
But if I do the same in Excel with formula 4/(2*(37/2))*100, it returns 10.81.
This is because Excel doesn't use integer division. 37 / 2 is 18.5, 18.5 * 2 is 37. 4 / 37 is 0.108108108... Thus multiplying by 100 gives approximately 10.81.
Explanation
The reason it's doing integer division is because you have an integer period. Java sees that there are two integers and are being divided so it uses integer division. This causes all fractional parts to be erased, meaning the 0.5 from 18.5 resulting from 37 / 2 is removed, making the results different.
Fix
You can 'fix' this by making at least one of the operands in the division float or double:
double percent = absence/(week*((double) 37/period))*100;
What this will do is achieve floating-point division giving 10.8108...

Declare all of the variables as double and it will work as you expect:
public static void main(String[] args) {
double period = 2;
double absence = 4;
double week = 2;
double percent = absence /(week *(37/period ))*100;
System.out.println("percent: " + percent);
}
Gives the output:
percent: 10.81081081081081
If your period variable is declared as an int, your output will change to:
percent: 11.11111111111111
This is becuase you're losing precision due to rounding down when you divide 37 by the int period. If you change it to double, it will give you the expected result.

Related

Floating point precision with Math.Ceil (Java)

I'm implementing a business rule to calculate a percentage increase on a stock level:
Stock level | Percentage Increase | Expected output
100 | 93 | 193
As decimal stock levels are not well defined the rule is to round up the output:
public int calculateStockLevel(int rawStockLevel, double percentageIncrease) {
return Math.ceil(rawStockLevel * (1 + (percentageIncrease / 100));
}
Actual output for the situation above: 194 (The test fails)
This looks like a floating point precision error, whats an elegant & readable way to get this test to pass?
You should use BigDecimal to specify what precision you want, but for your case you could do something simpler: just divide by 100 at the end.
public int calculateStockLevel(int rawStockLevel, double percentageIncrease) {
return Math.ceil(rawStockLevel * (100 + percentageIncrease) / 100);
}
Binary floating-point types can't represent exactly every value in decimal. So percentageIncrease / 100 will get the closest representation in binary. In your case the exact value of 0.93 in double precision is 0.930000000000000048849813083507, which is slightly more than the true decimal value. Therefore after ceil it'll be rounded up to 0.94
If you want exact decimal output you must use decimal arithmetic, like BigDecimal. But in your case you can just do it in plain integer, utilizing the fact that the fractional part of the result must be larger than 0 if remainder of the division is non-zero.
int temp = rawStockLevel * (100 + percentageIncrease);
int result = temp/100;
if (temp % 100 != 0)
result++; // round up if not divisible by 100
return result;

printing wrong decimal

arr[i]=s+t-Math.abs(s-j)/2;
here is the line, s= 600 j = 631 t =60 , the answer should be 645.5 but it prints 645.0.
arr[] is double
Assuming that the types of s and j are int, you must be performing Java's integer division, which results in an int. s-j results in -31, and the Math.abs overload that takes an int is called, and 31 is returned. Integer division in Java means that 31/2 is 15 the int, not 15.5 the double.
Use the floating-point literal 2.0 to force floating-point division (or cast one of the / operands to a double):
arr[i]=s+t-Math.abs(s-j)/2.0;
Its because you are losing precision while dividing ints .Put a cast around the division
arr[i]= s+ t- (double) Math.abs(s-j)/2;
Try putting.
arr[i]=s+t-Math.abs(s-j)/2.0;
The trick here is that when you do divide by 2 it will give you an integer answer. So for your case( Math.abs(-31)/2 -> 15 instead of 15.5. and thus will truncate the .5
Therefore, by adding the 2.0 it will give you a double answer that is 15.5 and give you the desired solution
Probably s-j is an integer-valued (or long-valued) expression in your example, which makes Java select an integer/long valued Math.abs(...) call, returning an integer/long value. When dividing integer values, the decimal places are truncated. For example, 3 / 2 becomes 1, 15 / 2 becomes 7.
An easy way to make the expression evaluated with floating point math, is to divide by 2.0 instead:
arr[i] = s + t - Math.abs(s-j) / 2.0;
These other writing styles will work too:
arr[i] = s + t - Math.abs(s-j) / 2.;
arr[i] = s + t - Math.abs(s-j) / 2f;
arr[i] = s + t - Math.abs(s-j) / 2d;
You should set 2f in order to make a decimal division (now you are doing a integer division in Math.abs(s-j)/2)
Then your code should be
arr[i] = s + t - Math.abs(s-j)/2f;

How can I round manually?

I'd like to round manually without the round()-Method.
So I can tell my program that's my number, on this point i want you to round.
Let me give you some examples:
Input number: 144
Input rounding: 2
Output rounded number: 140
Input number: 123456
Input rounding: 3
Output rounded number: 123500
And as a litte addon maybe to round behind the comma:
Input number: 123.456
Input rounding: -1
Output rounded number: 123.460
I don't know how to start programming that...
Has anyone a clue how I can get started with that problem?
Thanks for helping me :)
I'd like to learn better programming, so i don't want to use the round and make my own one, so i can understand it a better way :)
A simple way to do it is:
Divide the number by a power of ten
Round it by any desired method
Multiply the result by the same power of ten in step 1
Let me show you an example:
You want to round the number 1234.567 to two decimal positions (the desired result is 1234.57).
x = 1234.567;
p = 2;
x = x * pow(10, p); // x = 123456.7
x = floor(x + 0.5); // x = floor(123456.7 + 0.5) = floor(123457.2) = 123457
x = x / pow(10,p); // x = 1234.57
return x;
Of course you can compact all these steps in one. I made it step-by-step to show you how it works. In a compact java form it would be something like:
public double roundItTheHardWay(double x, int p) {
return ((double) Math.floor(x * pow(10,p) + 0.5)) / pow(10,p);
}
As for the integer positions, you can easily check that this also works (with p < 0).
Hope this helps
if you need some advice how to start,
step by step write down calculations what you need to do to get from 144,2 --> 140
replace your math with java commands, that should be easy, but if you have problem, just look here and here
public static int round (int input, int places) {
int factor = (int)java.lang.Math.pow(10, places);
return (input / factor) * factor;
}
Basically, what this does is dividing the input by your factor, then multiplying again. When dividing integers in languages like Java, the remainder of the division is dropped from the results.
edit: the code was faulty, fixed it. Also, the java.lang.Math.pow is so that you get 10 to the n-th power, where n is the value of places. In the OP's example, the number of places to consider is upped by one.
Re-edit: as pointed out in the comments, the above will give you the floor, that is, the result of rounding down. If you don't want to always round down, you must also keep the modulus in another variable. Like this:
int mod = input % factor;
If you want to always get the ceiling, that is, rounding up, check whether mod is zero. If it is, leave it at that. Otherwise, add factor to the result.
int ceil = input + (mod == 0 ? 0 : factor);
If you want to round to nearest, then get the floor if mod is smaller than factor / 2, or the ceiling otherwise.
Divide (positive)/Multiply (negative) by the "input rounding" times 10 - 1 (144 / (10 * (2 - 1)). This will give you the same in this instance. Get the remainder of the last digit (4). Determine if it is greater than or equal to 5 (less than). Make it equal to 0 or add 10, depending on the previous answer. Multiply/Divide it back by the "input rounding" times 10 - 1. This should give you your value.
If this is for homework. The purpose is to teach you to think for yourself. I may have given you the answer, but you still need to write the code by yourself.
Next time, you should write your own code and ask what is wrong
For integers, one way would be to use a combination of the mod operator, which is the percent symbol %, and the divide operator. In your first example, you would compute 144 % 10, resulting in 4. And compute 144 / 10, which gives 14 (as an integer). You can compare the result of the mod operation to half of the denominator, to find out if you should round the 14 up to 15 or not (in this case not), and then multiply back by the denominator to get your answer.
In psuedo code, assuming n is the number to round, p is the power of 10 representing the position of the significant digits:
denom = power(10, p)
remainder = n % denom
dividend = n / denom
if (remainder < denom/2)
return dividend * denom
else
return (dividend + 1) * denom

unexpected results with decimal arithmetic expression

I have some business logic with arithmetic expression and their results are as follows
(10.0 * 20.58)/1.0=205.7999..98
(6.0 * 37.9)/1.0=227.3999..98
(5.0 * 50.0)/1.0=250.0
(10.0 * 37.9)/1.0=379.0
But expected results are
(10.0 * 20.58)/1.0=205.8
(6.0 * 37.9)/1.0=227.4
(5.0 * 50.0)/1.0=250.0
(10.0 * 37.9)/1.0=379.0
I am not clear why we are getting that .999..98 fraction part? Due to that my equals comparison is failing and so business logic. For few cases we are using
amt = (double)Math.round(orderAmt*100000)/100000;
But that is not possible to do the same in each and every place where we have double arithmetic expression.
I want to know why we get such results randomly and is there any possibility to round the results to 5 decimal places instead of rounding every where?
With radix 10 there are some fractions who can't be expressed exactly with a finite number of digits, like for example 1/3 = 0.33333333....
It's the same with radix 2, except that the dividers that produce this kind of results are not the one we are accustomed to, and for example, for 20.58, which is 2058 / 100, it is the case.
Internally, doubles and floats are stored with bits (an not digit), so the exact value of the double or float just can't be stored in the computer's memory. Each time you perform an operation with this value, you get a small shift, because of the approximation, which becomes visible when converting back to decimal format for printing.
It's something you have to pay attention while perfoming computations where precision is important.
So you have two solutions:
Store all your numbers in decimal type and perform all your calculation with it. This will achieve accuracy but for the price of performance.
You can also keep all the calculation with double or float, and format with a fixed number of digits only for printing results.
You could use BigDecimal for roundoff
BigDecimal bd = new BigDecimal((10.0 * 20.58)/1.0) ;
bd = bd.setScale(4, RoundingMode.UP);
use with a static method
public static double round(double value, int digits) {
BigDecimal bd = new BigDecimal(value);
return bd.setScale(digits, RoundingMode.UP).doubleValue();
}
RoundingMode has :
RoundingMode.UP;
RoundingMode.DOWN;
RoundingMode.HALF_DOWN;
RoundingMode.HALF_EVEN;
The basic problem is that
System.out.println(10.0 * 20.58);
prints
205.79999999999998
has a small rounding error due to a representation error in 20.58
You either need to
round the result before comparing.
use a comparision which allows for some error
use BigDecimal (which is over kill in most cases)
use cents instead of dollars i.e. use int or long with fixed precision.
In the last case, the same operation would read
System.out.println(10 * 2058);
prints
20580
where this is 100x the value you need as its fixed precision e.g. cents instead of dollars.
You may want to use double with rounding as below:
double roundingPlaces = 10.0;//use 10.0 for single decimal digit rounding
double a1 = 10.0;
double b1 = 20.58;
double c1 = 1.0;
System.out.println(Math.round((a1*b1*roundingPlaces)/c1)/roundingPlaces);
This prints 205.8.
float fa1 = (float) 10.0;
float fb1 = (float)20.58;
float fc1 = (float)1.0;
System.out.println(fa1*fb1/fc1);
This also prints 205.8
Use float instead of double
http://ideone.com/L9vwR8
System.out.println((float)((10.0f * 20.58f)/1.0f));
output
205.8

why is the Double.parseDouble making 9999999999999999 to 10000000000000000? [duplicate]

This question already has answers here:
How to resolve a Java Rounding Double issue [duplicate]
(13 answers)
Closed 9 years ago.
why is the Double.parseDouble making 9999999999999999 to 10000000000000000 ?
For Example :
Double d =Double.parseDouble("9999999999999999");
String b= new DecimalFormat("#.##").format(d);
System.out.println(b);
IS Printing
10000000000000000
instead it has to show 9999999999999999 or 9999999999999999.00
Any sort of help is greatly appreciated.
The number 9999999999999999 is just above the precision limit of double-precision floating-point. In other words, the 53-bit mantissa is not able to hold 9999999999999999.
So the result is that it is rounded to the nearest double-precision value - which is 10000000000000000.
9999999999999999 = 0x2386f26fc0ffff // 54 significant bits needed
10000000000000000 = 0x2386f26fc10000 // 38 significant bits needed
double only has 15/16 digits of accuracy and when you give it a number it can't represent (which is most of the time, even 0.1 is not accurate) it takes the closest representable number.
If you want to represent 9999999999999999 exactly, you need to use BigDecimal.
BigDecimal bd = new BigDecimal("9999999999999999");
System.out.println(new DecimalFormat("#.##").format(bd));
prints
9999999999999999
Very few real world problems need this accuracy because you can't measure anything this accurately anyway. i.e. to an error of 1 part per quintillion.
You can find the largest representable integer with
// search all the powers of 2 until (x + 1) - x != 1
for (long l = 1; l > 0; l <<= 1) {
double d0 = l;
double d1 = l + 1;
if (d1 - d0 != 1) {
System.out.println("Cannot represent " + (l + 1) + " was " + d1);
break;
}
}
prints
Cannot represent 9007199254740993 was 9.007199254740992E15
The largest representable integer is 9007199254740992 as it needs one less bit (as its even)
9999999999999999 requires 54 bits of mantissa in order to be represented exactly, and double only has 52. The number is therefore rounded to the nearest number that can be represented using a 52-bit mantissa. This number happens to be 10000000000000000.
The reason 10000000000000000 requires fewer bits is that its binary representation ends in a lot of zeroes, and those zeroes can get represented by increasing the (binary) exponent.
For detailed explanation of a similar problem, see Why is (long)9223372036854665200d giving me 9223372036854665216?

Categories

Resources