Metric Convertions Algorithm without "if-else" or "switch-case" - java

I want to write a program which can converts one unit to another unit. Let's say I have 2 methods.First method can do metric conversions, second method can do weight conversitons. For example;
1. long km=metricConvLength(long mil,Enum.mil,Enum.km);//first method
2. long agirlik=metricConvWeight(long kg,Enum.mil,Enum.km);//second method
I want to use Enum struct for these variables.
My program can convert these things and opposites;
sea mile-km
sea mile-mile
feet- km
feet- mil
pound- kg
ons- gr
inc - cm
yard - m
knot- km
My Question: I don't want to use if-else or switch-case structs for conversions.(Because if I use if-else struct,my code looks like so bad, much easy and slow.And I need more then 50 if-else struct when if I use these struct.This is grind.)
Can I write an algorithm for these conversions without using if-else or switch-case.
My purpose is less code, more work. Any tips about algorithm?

You do not need an if-then-else - in fact, you do not need control statements in your program. All you need is a lookup table - a Map that translates your unit enum to a double conversion factor, such that multiplying the measure in units by the conversion factor you get meters for units of space, and kilos for units of weight. Conversely, dividing meters by that factor gives you the desired units.
With this map in hand, you can do conversions for all pairs of units:
Look up the conversion factor Cs for the source units
Look up the conversion factor Cd for the destination units
Return value * Cs / Cd as your result.
For example, let's say that you want to deal with meters, yards, inches, and feet. Your map would look like this:
m - 1.0
y - 0.9144
in - 0.0254
ft - 0.3048
Now let's say you want to convert 7.5 yards to feet:
Look up Cs = 0.9144
Look up Cd = 0.3048
Compute and return Res = 7.5 * 0.9144 / 0.3048 = 22.5

I would suggest to introduce a type "MetricValue". The history of software engineering is full of projects where programmers forgot the units of the values they used. The resulting desasters are wellknown.
Moreover, I would propose an immutable class as it makes many design aspects more simple.
Defining a base unit helps you a lot as you first convert all values to it and then convert the value to your target unit. You could also apply a matrix but then you would need to precalculate the values. The size of your matrix will have the size of n^2 while the conversion from and to the base unit remains only n values. If you use complicated conversion formulas which are resource-consuming, then it makes sense. Otherwise, the benefit will be (too) small regarding the efforts.
What about this code?
public class MetricValue {
public static enum Unit {
m(1.0d), y(0.9144d), in(0.0254d), ft(0.3048d);
final static Unit baseUnit = m;
final double perBaseUnit;
private Unit(double inM) {
this.perBaseUnit = inM;
}
public double fromBaseUnit(double value) {
return value / perBaseUnit;
}
public double toBaseUnit(double value) {
return value * perBaseUnit;
}
}
final double value;
final Unit unit;
public MetricValue(double value, Unit unit) {
super();
this.value = value;
this.unit = unit;
}
public MetricValue to(Unit newUnit) {
Unit oldUnit = this.unit;
return new MetricValue(newUnit.fromBaseUnit(oldUnit.toBaseUnit(value)),
newUnit);
}
#Override
public String toString() {
return value + " " + unit.name();
}
public static void main(String[] args) {
MetricValue distanceInM = new MetricValue(1, Unit.m);
MetricValue distanceInFt = new MetricValue(6, Unit.ft);
System.out.println(distanceInM);
System.out.println(distanceInM.to(Unit.y));
System.out.println(distanceInM.to(Unit.y).to(Unit.m));
System.out.println(distanceInM.to(Unit.y).to(Unit.ft));
System.out.println(distanceInFt.to(Unit.m));
}
}
You can use the raw value and create a new metric value when you calculate. You could also add operations for basic or sophisticated arithmetic operations.
The getters were omitted here.

Associate their relative value to variables in your Enums (I guess you have one for distances, one for weights) like enum Distances { CM(100), M(100*1000), IN(254) },... and then you just have to get the ratio of the values provided, multiply by the first param, and you have your result. Use a base 100 or 1000 for the smallest unit if you want a decent precision.

In your Enum you could implement a fromString method on an enum type, Lets say your Enum name is 'Distances'
private static final Map<String,Distances> stringToEnum = new HashMap<String,Distances>;
Initialize the map:
static{
for(Distances distance: values()) {
stringToEnum.put(distance.toString(),distance);
}
}
Return enumType for String:
public static Distances getDistance(String stringValue){
return stringToEnum.get(stringValue);
}
In your methods you could just pass in:
Distance.getDistance("feet").getCalculatedDistance()

Choose one of the units to be the "base" unit (one for weight, e.g., gr; and one for distance, e.g., m). Then add methods toBaseUnit and fromBaseUnit to your enum using conversion ratios for each of your values.
No "ifs" involved, and your conversion methods will look like this:
ResultingUnit.fromBaseUnit(originalUnit.toBaseUnit(value));
Sample enum:
public enum Distance {
METER(new BigDecimal("1.0")), // Base Unit is METER
KM(new BigDecimal("1E3")),
CM(new BigDecimal("1E-2"));
private final static MathContext MC =
new MathContext(30, RoundingMode.HALF_EVEN);
private final BigDecimal conversionRatio;
Distance(BigDecimal conversionRatio) {
this.conversionRatio = conversionRatio;
}
long fromBaseUnit(BigDecimal baseUnit) {
return baseUnit.divide(conversionRatio, MC).longValue();
}
// returns BigDecimal to avoid rounding two times
// and possible division by zero
BigDecimal toBaseUnit(long originalUnit) {
return BigDecimal.valueOf(originalUnit).multiply(conversionRatio);
}
}
And the adapted conversion method:
public long metricConvLength(long value, Distance orgUnit, Distance resultUnit) {
return resultUnit.fromBaseUnit(orgUnit.toBaseUnit(value));
}
Besides being slightly faster (by avoiding Map lookups and looping through the enum values), the major advantage of this approach is that if you ever need more complex conversion operations (say, a Temperature enum with Celsius, Farenheit and Kelvin) you can override fromBaseUnit and toBaseUnit in the enum value body.
Working Example.
I've used BigDecimal for the conversion ratio and internal calculations in order to have greater control over precision and rounding behavior.

Related

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.

Parse Coefficient of an Linear equation

In java i am trying to find the coefficients of a linear equation to find solution of linear equation in my calculator application for example :
3x +2*(6x-3) = 2 -4x
what i am dying to get is the coefficients of x and the constant in the form ax+b =0 ,
in this particular example
coefficient = 19
constant = -8
Please suggest a generalized idea
As already suggested by my comment: This may be arbitrarily complicated, depending on what exactly this parser should support. There are several potentially very complex and challenging tasks involved here.
The first one is parsing itself. Although it's well understood and there are supporting tools for writing parsers and all, it would be tedious (and would involve some effort) to write a robust, reliable parser for these expressions from scratch.
The second one is simplifying the expression. While one could write a simple parser (or use an existing one), one might (at the first glance) think that it is necessary to perform manipulations on the generated AST in order to find the actual constant and coefficient - e.g. one could think that it would be necessary to apply the laws of distribution, find common factors, shift partial expression from one side of the equation to the other and so on.
Fortunately, all this is not necessary :-)
You can use an arbitrary parser to parse the expressions that are involved in the equation. One of the most well-known parsers is JEP, the Java Expression Parser (this is not a recommendation - I just knew it, and it seems to work well). As the name suggests, it's only an Expression parser, and not an Equation parser. But the equation can simply be split at the = in order to obtain two expressions that can be parsed individually.
The two expressions would not be sufficient in order to find the coefficient and constant. But here, a small (dirty?) trick comes into play: You can derive the coefficient and constants by evaluating these expressions. Particularly, you can once set x=0 to determine the constant part of the left and right side, respectively. Then, you can set x=1, evaluate the resulting expression, and subtract the constant in order to obtain the coefficient.
From the coefficients and constants of both sides, you can compute the coefficient and constant of the whole equation. This is implemented here, as a MCVE:
import org.nfunk.jep.JEP;
public class LinearEquationParser
{
private double coefficient;
private double constant;
public static void main(String[] args)
{
runTest("3x = 5");
runTest("3x +2*(6x-3) = 2 -4x");
runTest("3x + 2*(6x -sin(3))=cos(2)-4*x*log(tan(43))");
}
private static void runTest(String s)
{
System.out.println("Input: "+s);
LinearEquationParser p = new LinearEquationParser();
p.process(s);
System.out.println("Coefficient: "+p.getCoefficient());
System.out.println("Constant : "+p.getConstant());
System.out.println();
}
public void process(String s)
{
JEP jep = new JEP();
jep.setImplicitMul(true);
jep.addStandardFunctions();
jep.addStandardConstants();
jep.addVariable("x", 0.0);
String s0 = s.substring(0, s.indexOf("="));
String s1 = s.substring(s.indexOf("=")+1, s.length());
jep.parseExpression(s0);
if (jep.hasError())
{
throw new IllegalArgumentException(jep.getErrorInfo());
}
jep.addVariable("x", 0.0);
double constant0 = jep.getValue();
jep.addVariable("x", 1.0);
double value0 = jep.getValue();
jep.parseExpression(s1);
if (jep.hasError())
{
throw new IllegalArgumentException(jep.getErrorInfo());
}
jep.addVariable("x", 0.0);
double constant1 = jep.getValue();
jep.addVariable("x", 1.0);
double value1 = jep.getValue();
constant = constant0 - constant1;
coefficient = (value0 - constant0) - (value1-constant1);
}
public double getCoefficient()
{
return coefficient;
}
public double getConstant()
{
return constant;
}
}
The output is, as desired:
Input: 3x = 5
Coefficient: 3.0
Constant : -5.0
Input: 3x +2*(6x-3) = 2 -4x
Coefficient: 19.0
Constant : -8.0
Input: 3x + 2*(6x -sin(3))=cos(2)-4*x*log(tan(43))
Coefficient: 15.7024963786418
Constant : 0.13390682042740798

Java - Costum Classes, code not working

For some reason when I run the file the answer seems to be 0. I'm still fairly new to Java so could someone explain to me what I've done wrong.. It all seems fine to me.
public class bus {
public static void main(String[] args) {
bus fivePM = new bus(23, 120);
bus elevenAM = new bus(27, 140);
System.out.println(fivePM.gallonsUsed());
System.out.println(elevenAM.gallonsUsed());
}
private int mpg;
private int milesTravelled;
private double used;
public bus(int mpg, int milesTravelled){
this.mpg = mpg;
this.milesTravelled = milesTravelled;
}
public double gallonsUsed(){
this.used = this.mpg/this.milesTravelled;
return this.used;
}
}
In both of your instantiated bus objects, milesTravelled is less than mpg.
When you divide an int by an int, you get an int.
this.mpg/this.milesTravelled;
This will return 0 because the digits after the decimal point don't matter if it's an int.
To make them not ints, you could do things like:
this.mpg * 1.0 / this.milesTravelled
or
this.mpg/((double) this.milesTravelled)
As mentioned, diving ints gives an integer number. You can change type types of mpg and milesTravelled to double. Also you should be inverting the answer. m/g divided by m = m/mg = 1/g, not g. For example, at 10mpg traving 20m would use 2 gallons, but your calculation would give 1/2 gallon.
Use double instead of int or just carefully convert your int into a double before doing any division. This is a common problem for Java novices. Take a look at this and this. Try this out:
private double mpg;
private double milesTravelled;
private double used;
public bus(double mpg, double milesTravelled){
this.mpg = mpg;
this.milesTravelled = milesTravelled;
}
NOTE 1: When you do the division, make sure you check to see if milesTravelled is equal to 0 or not. If it is, then you will get an Exception.
NOTE 2: Change bus to Bus in the header definition of bus (which then means you will use Bus instead of bus). Using the capital letter in the beginning of a custom Object's name is a relatively common standard.

Java double and working with really small values

I have to store the product of several probabilty values that are really low (for example, 1E-80). Using the primitive java double would result in zero because of the underflow. I don't want the value to go to zero because later on there will be a larger number (for example, 1E100) that will bring the values within the range that the double can handle.
So, I created a different class (MyDouble) myself that works on saving the base part and the exponent parts. When doing calculations, for example multiplication, I multiply the base parts, and add the exponents.
The program is fast with the primitive double type. However, when I use my own class (MyDouble) the program is really slow. I think this is because of the new objects that I have to create each time to create simple operations and the garbage collector has to do a lot of work when the objects are no longer needed.
My question is, is there a better way you think I can solve this problem? If not, is there a way so that I can speedup the program with my own class (MyDouble)?
[Note: taking the log and later taking the exponent does not solve my problem]
MyDouble class:
public class MyDouble {
public MyDouble(double base, int power){
this.base = base;
this.power = power;
}
public static MyDouble multiply(double... values) {
MyDouble returnMyDouble = new MyDouble(0);
double prodBase = 1;
int prodPower = 0;
for( double val : values) {
MyDouble ad = new MyDouble(val);
prodBase *= ad.base;
prodPower += ad.power;
}
String newBaseString = "" + prodBase;
String[] splitted = newBaseString.split("E");
double newBase = 0; int newPower = 0;
if(splitted.length == 2) {
newBase = Double.parseDouble(splitted[0]);
newPower = Integer.parseInt(splitted[1]);
} else {
newBase = Double.parseDouble(splitted[0]);
newPower = 0;
}
returnMyDouble.base = newBase;
returnMyDouble.power = newPower + prodPower;
return returnMyDouble;
}
}
The way this is solved is to work in log space---it trivialises the problem. When you say it doesn't work, can you give specific details of why? Probability underflow is a common issue in probabilistic models, and I don't think I've ever known it solved any other way.
Recall that log(a*b) is just log(a) + log(b). Similarly log(a/b) is log(a) - log(b). I assume since you're working with probabilities its multiplication and division that are causing the underflow issues; the drawback of log space is that you need to use special routines to calculate log(a+b), which I can direct you to if this is your issue.
So the simple answer is, work in log space, and re-exponentiate at the end to get a human-readable number.
You trying to parse strings each time you doing multiply. Why don't you calculate all values into some structure like real and exponential part as pre-calculation step and then create algorithms for multiplication, adding, subdivision, power and other.
Also you could add flag for big/small numbers. I think you will not use both 1e100 and 1e-100 in one calculation (so you could simplify some calculations) and you could improve calculation time for different pairs (large, large), (small, small), (large, small).
You can use
BigDecimal bd = BigDecimal.ONE.scaleByPowerOfTen(-309)
.multiply(BigDecimal.ONE.scaleByPowerOfTen(-300))
.multiply(BigDecimal.ONE.scaleByPowerOfTen(300));
System.out.println(bd);
prints
1E-309
Or if you use a log10 scale
double d = -309 + -300 + 300;
System.out.println("1E"+d);
prints
1E-309.0
Slowness might be because of the intermediate string objects which are created in split and string concats.
Try this:
/**
* value = base * 10 ^ power.
*/
public class MyDouble {
// Threshold values to determine whether given double is too small or not.
private static final double SMALL_EPSILON = 1e-8;
private static final double SMALL_EPSILON_MULTIPLIER = 1e8;
private static final int SMALL_EPSILON_POWER = 8;
private double myBase;
private int myPower;
public MyDouble(double base, int power){
myBase = base;
myPower = power;
}
public MyDouble(double base)
{
myBase = base;
myPower = 0;
adjustPower();
}
/**
* If base value is too small, increase the base by multiplying with some number and
* decrease the power accordingly.
* <p> E.g 0.000 000 000 001 * 10^1 => 0.0001 * 10^8
*/
private void adjustPower()
{
// Increase the base & decrease the power
// if given double value is less than threshold.
if (myBase < SMALL_EPSILON) {
myBase = myBase * SMALL_EPSILON_MULTIPLIER;
myPower -= SMALL_EPSILON_POWER;
}
}
/**
* This method multiplies given double and updates this object.
*/
public void multiply(MyDouble d)
{
myBase *= d.myBase;
myPower += d.myPower;
adjustPower();
}
/**
* This method multiplies given primitive double value with this object and update the
* base and power.
*/
public void multiply(double d)
{
multiply(new MyDouble(d));
}
#Override
public String toString()
{
return "Base:" + myBase + ", Power=" + myPower;
}
/**
* This method multiplies given double values and returns MyDouble object.
* It make sure that too small double values do not zero out the multiplication result.
*/
public static MyDouble multiply(double...values)
{
MyDouble result = new MyDouble(1);
for (int i=0; i<values.length; i++) {
result.multiply(values[i]);
}
return result;
}
public static void main(String[] args) {
MyDouble r = MyDouble.multiply(1e-80, 1e100);
System.out.println(r);
}
}
If this is still slow for your purpose, you can modify multiply() method to directly operate on primitive double instead of creating a MyDouble object.
I'm sure this will be a good deal slower than a double, but probably a large contributing factor would be the String manipulation. Could you get rid of that and calculate the power through arithmetic instead? Even recursive or iterative arithmetic might be faster than converting to String to grab bits of the number.
In a performance heavy application, you want to find a way to store basic information in primitives. In this case, perhaps you can split the bytes of a long or other variable in so that a fixed portion is the base.
Then, you can create custom methods the multiply long or Long as if they were a double. You grab the bits representing the base and exp, and truncate accordingly.
In some sense, you're re-inventing the wheel here, since you want byte code that efficiently performs the operation you're looking for.
edit:
If you want to stick with two variables, you can modify your code to simply take an array, which will be much lighter than objects. Additionally, you need to remove calls to any string parsing functions. Those are extremely slow.

Java: public static final double can't be set to decimal fraction?

I have a config file which includes some factors I want to use for calculations.
public class Config {
public static final double factor = 67/300; // ~0,2233...
}
Im accessing the factors like this:
public class Calculate {
public static calc() {
...
result *= Config.factor;
...
When I do that Config.factor equals 0, so my result is 0, too. I don't have that problem if I set the factor to 0.2233, but that wouldn't be as accurate. Why doesn't setting it to 67/300 work?
Try this:
public static final double factor = 67/300d;
The problem is that 67 and 300 are integer literals, so the division ends up being an integer, which is 0. The d at the end of the number makes it a double literal, so the result of 67/300d is a double.
Note that in the previous code the double literal is 300d. You can also use 67d/300 or 67d/300d.
It should be something like below:
public static final double factor = 67d/300d;
If you don't append 'd' it will be treated as integer that is why you are getting ZERO.
int will be deafault choice. As per doc
For integral values, this data type is generally the default choice unless there is a reason (like the above) to choose something else
When you enter 67/300, the compiler treats these as int rather than double. Therefore, when the division occurs, the result is floored to 0.
To avoid this, cast the numbers to double either by adding a d after each number (as described in the other answers) or a full type cast with (double).

Categories

Resources