Cumulative difference with fixed initial value - java

I'm a newbie to Java and have to implement a solution at work to calculate cumulative difference.
I am extracting data from a flat file using Informatica powercenter. One of the columns is total deductions for a department. The logic required to transform data in this column is below.
If Total deductions<=9999999.99, then display the value as is, ie 9999999.99
If Total deductions>9999999.99, then display 9999999.99 and in the next line display the difference between 9999999.99 and incoming value.
For ex, if incoming value is 10000000.99, then display
9999999.99
1
If total deductions = 20000000.98 then display the below
9999999.99
9999999.99
1
I have the below code where I am hard coding values, and feel like this can be accomplished dynamically.
package day1.examples;
public class MedicalCenter {
public static void main(String[] args) {
double v=9999999.99;
double i=20000000.98;
if (i<v) {
System.out.println(i);
}
if (i>v && i<=9999999.99*2) {
System.out.println(9999999.99);
System.out.println(i-v);
}
if (i>v && i<=9999999.99*3) {
System.out.println(9999999.99);
System.out.println(9999999.99);
System.out.println(i-9999999.99*2);
};
}
}
Sample Output:
9999999.99
9999999.99
1.0

You need to either keep subtracting by 9,999,999.99 until you get the remainder, or you can use division. I'll show you an example of subtraction.
public static void main(String[] args) {
double input; //need to implement this
double v=9999999.99;
while(input > v) {
System.out.print(v + " ");
input -= v; //subtract
}
System.out.println(input);
}
The idea is that you keep subtracting v from the input number until you can't subtract anymore, and each time you subtract you also print out v together with a space. After you're done subtracting, you simply print the remainder of the input.
As far as I know, division is just a glorified form of subtraction in the end, so this is possibly simpler than using division—not only by producing cleaner code (which is often the highest priority), but perhaps also performance wise.
You will have floating point arithmetic issues due to the subtraction however, so you should use BigDecimal instead of double. More information on that here: Is floating point math broken?

Related

Does java do some round off to avoid imprecision in IEEE-754 representation

I recently came across a problem where I had to think about the imprecision in representing floats. But to my surprise, I didn't find the errors that I expected. I created a simple example code for describing my question
public class Main {
public static void main(String[] args) {
float one_point_six = 1.6f; // According to IEEE-754, should be 1.60000002384185791015625
float zero_point_four = 0.4f; // According to IEEE-754, should be 0.4000000059604644775390625
float sum = one_point_six + zero_point_four; // shouldn't this be 2.0000000298023223876953125?
System.out.println(Math.ceil(sum)); // Prints 2.0. Shouldn't this be 3?
System.out.println(sum > 2f); // Prints false. Why?
}
}
The output remains the same even if I replace floats with doubles. Is Java doing some round-offs or am I missing something?
I used https://www.h-schmidt.net/FloatConverter/IEEE754.html for finding out the "actual" value that's being stored.
shouldn't this be 2.0000000298023223876953125?
No. As you know, float has limited precision, so it is not precise enough to store a number of that precision. Trying using your converter, and entering 2.0000000298023223876953125 in there, you will find that the actual value stored is just "2".

java removing trailing decimal digits causing .0 become .99

I want to simply have a function that converts a double with as many decimal places into 4 decimal places without rounding.
I have this code that has been working fine but found a random instance where it turned .0 into .99
Here are some sample outputs
4.12897456 ->4.1289
4.5 ->4.5
4.5231->4.5231
5.53->5.53
5.52->5.199 (Wrong conversion, I want it to be 5.52)
private static double get4Donly(double val){
double converted = ((long)(val * 1e4)) / 1e4;
return converted
}
EDIT: This conversion is called thousands of times, so please suggest a method where I dont have to create a new string all the time.
You can use DecimalFormat
import java.text.DecimalFormat;
import java.math.RoundingMode;
import java.util.Arrays;
public class MyClass {
public static void main(String args[]) {
DecimalFormat df = new DecimalFormat("#.####");
df.setRoundingMode(RoundingMode.DOWN);
for (Number n : Arrays.asList(4.12897456, 4.5, 4.5231, 5.53, 5.52)) {
Double d = n.doubleValue();
System.out.println(df.format(d));
}
}
}
RoundingMode.DOWN rounds towards zero, new DecimalFormat("#.####") creates a DecimalFormat instance that formats numbers to a maximum of 4 decimal places. Put those two together and the above code produces the following output, which I believe matches your expectations:
4.1289
4.5
4.5231
5.53
5.52
Doubles just don't work like you think they do.
They are stored in a binary form, not a decimal form. Just like '1 divided by 3' is not representable in a decimal double (0.3333333333 is not enough, it's infinite 3s, so not representable, so you get a rounding error), but '1 divided by 5' is representable just fine, there are numbers that are representable, and numbers that end up rounded when storing things in a double type, but crucially things that seem perfectly roundable in decimal may not be roundable in binary.
Given that they don't match up, your idea of 'eh, I will multiply by 4, turn it to a long, then convert back to a double, then divide by 1000' is not going to let those digits go through unmolested. This is not how you round things, as you're introducing additional loss in addition to the loss you already started out with due to using doubles.
You have 3 solutions available:
Just print it properly
A double cannot be considered to 'have 4 digits after the decimal separator' because a double isn't decimal.
Therefore, it doesn't even make sense to say: Please round this double to at most 4 fractional digits.
That is the crucial realisation. Once you understand that you'll be well along the way :)
What you CAN do is 'please take this double and print it by using no more than 4 digits after the decimal separator'.
String out = String.format("%.4f", 5.52);
or you can use System.printf(XXX) which is short for System.print(String.format(XXX)).
This is probably what you want
forget doubles entirely
For some domains its better to ditch doubles and switch to longs or ints. For example, if you're doing finances, it's better to store the atomic unit as per that currency in a long, and forego doubles instead. So, for dollars, store cents-in-a-long. For euros, the same. For bitcoin, store satoshis. Write custom rendering to render back in a form that is palatable for that currency:
long value = 450; // $4.50
String formatCurrency(long cents) {
return String.format("%s%s%d.%02d", cents < 0 ? "-" : " ", "$", Math.abs(cents) / 100, Math.abs(cents) % 100);
}
Use BigDecimal
This is generally more trouble than it is worth, but it stores every digit, decimally - it represent everything decimal notation can (and it also cannot represent anything else - 1 divided by 3 is impossible in BigDecimal).
I would recommend using the .substring() method by converting the double to a String. It is much easier to understand and achieve since you do not require the number to be rounded.
Moreover, it is the most simple out of all the other methods, such as using DecimalFormat
In that case, you could do it like so:
private static double get4Donly(double val){
String num = String.valueOf(val);
return Double.parseDouble(num.substring(0, 6));
}
However, if the length of the result is smaller than 6 characters, you can do:
private static double get4Donly(double val){
String num = String.valueOf(val);
if(num.length()>6) {
return Double.parseDouble(num.substring(0, 6));
}else {
return val;
}
}

How to prevent Java from auto rounding up

I have a method which returns a double
public double getOdds() {
return odds;
}
This works completly fine. However, the problem is when the data is displayed, it automatically rounds the inserted value up. I want to prevant that, and get a value with 2 decimals.
Here are my JSP where I call the method. The ArrayList "bets" consist of all values entered by the user. But, as explained above, when the user enters 2.75 then it will return 3.0
ArrayList <bets> bets = betsDAO.getBets(x, y);
for (bets bet : bets) {
<td><%=bet.getOdds()%></td>
}
I'm still new to Java, and have tried looking for solutions, but unfortunatly I have not been able to solve the issue.
when the user enters 2.75 then it will return 3.0
The only reason for that to happen is if the value is limited to zero decimals at some point. Java only does that for int (or long or short or char or byte) values, and those are truncated, so result would be 2, not 3.0.
The only round-to-nearest I can envision is caused by the database or the JDBC driver:
The column type in the database is INTEGER or NUMBER(5,0) or something like that. Check your database schema.
The code reading the database (betsDAO) is calling getInt(...) and the JDBC driver rounds the value for you.
Anyway, the error is in code you haven't shown.
it depends on which data type you intend on working with, but this
should do the trick for you
public class Rounding {
public static void main(String[] args) {
//using string format
double input = 3.14159265359;
System.out.println("double : " + input);
System.out.println("double : " + String.format("%.2f", input));
//using Decimal format
double num = 1.34567;
DecimalFormat df = new DecimalFormat("#.###");
df.setRoundingMode(RoundingMode.CEILING);
System.out.println(df.format(num));
}
}
OUTPUT:
double : 3.14159265359
double : 3.14
double : 3.14
OUTPUT:
1.346
so you can be more specific here with the decimal point to return. it all depends on the importance of the decimal precision for your project
Couldn't Math.floor help with the rounding? it rounds down, and if you ever need to use rounding up then use Math.ceil.
hopefully this helps!

method is performing incorrect calculations on double argument

firstly, im sorry if this is a trivial question. I am a beginner and have been stuck on this for hours.
Below I have tried to create a unitizer method which has a series of if else statements. They are written in descending order, each time checking if a value can be divided by a given number, and if so, performing a division, rounding the value and adding an appropriate unit to the result.
in this question I have attempted to remove all unnecessary code, thus what i am presenting here is only a fragment of the unitizer method.
why is the unitizer method outputting values in hours, when the value should be in seconds?
For clarification, the expected value is ~ 4 seconds.
public class simplified
{
public static void main(String[] args)
{
int i = 5;
double n = Math.pow(2, (double) i);
System.out.println(a6(n)); // correctly displays the expected value.
System.out.println(unitizer(a6(n)));
}
public static double a6 (double n)
{
return Math.pow(2, n); // this value is in nanoseconds.
}
public static String unitizer (double x)
{
String time = "";
if (x/(60*60*1000*1000*1000) >= 1)
{
x = Math.round(x/(60*60*1000*1000*1000) * 100.0) / 100.0;
time = x + "hr ";
}
return time;
}
}
console output:
4.294967296E9
5.25hr
There is an int overflow at the expression 60*60*1000*1000*1000. This means, that the actual result 3,600,000,000,000 is too large to be stored as an int value and is therefore 'reduced' (mod 2^31) to 817,405,952.
This can be fixed by evaluating said expression in a 'larger' arithmetic, e.g. long. There is a nice little modifier, that will force exactly that:
60L*60*1000*1000*1000
^
In particular, it hints the compiler to interpret the preceding literal 60 as a long value and in consequence the whole calculation will be done in long arithmetic.
This modifier is by the way case-insensitive; however I prefer an upper-case L, because the lower-case letter l can easily be mistaken by the number 1.
With this change, the code will not enter the if-statement, because the value x is not larger than one hour. Most probably the omitted code of unitizer will deal with this case.
On a last side note, java has an in-built TimeUnit enum, which can do these conversions, too. However, it does so in long arithmetic and not in double arithmetic as it is required for this specific question.

factorial of non-integer value in java

5! = 5*4*3*2*1.
I have no problem with this. But I noticed with my program that if I type in say 3.5! it would return a defined number.
How do you calculate them?
I have something like this in my program
public class fact {
public static void main(String args[]) {
double factorial=1;
System.out.println("Type a number");
double number= sc.nextDouble(); /*I am using scanner*/
while (number !=0 ){
factorial = factorial * number;
number--;
}
System.out.println(factorial);
}
}
Factorial in its normal definition is defined only for positive integers. If you want to calculate factorials for any real numbers, have a look at Gamma functions.
https://en.wikipedia.org/wiki/Gamma_function
Thing is: factorial is pretty simple for whole, positive numbers.
The concept can also be applied for floating point numbers, but the math behind that could be considered advanced.
So: step back; and understand the math behind the concept; before implementing the concept!
In other words; you have to decide whether you intend to change your program to work with int numbers (validated to be > 0 ); or if you intend to allow floating point numbers. If the later is your goal; then your simple implementation won't do any more.
Beyond that: you want to study the concept of floating point numbers in the first place. It is a misconception to assume that a loop like
double number = ...
while (number !=0 ) {
..
number--;
}
would always stop when using floating point numbers instead of int/long! To the contrary ...
You are getting the defined value even for 3.5!, because your code will calculate it as following:
1*3.5*2.5*1.5*0.5
But actually it is wrong value for 3.5!. The correct value is: 11.6317283966. Your method is only valid for integer inputs. See this for more information about decimal factorials:
this link

Categories

Resources