EXP4J: can't get a good factorial operator working - java

I have the following factorial operator declared:
Operator factorial = new Operator("!", 1, true, Operator.PRECEDENCE_POWER + 1) {
#Override public double apply(double... args) {
final long arg = (long) args[0];
if ((double) arg != args[0]) {
throw new IllegalArgumentException("Operand for factorial has to be an integer");
}
if (arg < 0) {
throw new IllegalArgumentException("The operand of the factorial can not " +
"be " +
"less than zero");
}
double result = 1;
for (int i = 1; i <= arg; i++) {
result *= i;
}
return result;
}
};
So, it works fine when you have simple expressions such as 5! and so on, but things start to break when you try to emulate normal calculator behavior. The following example throws an IllegalArgumentException():
5!+5
Why? I guess it has something to do with 2 adjacent operators or something, since using (5!)+5 works with no issues.
This is devastating for the project I'm working on. Someone has suggested converting the operator into a function but that would take too much time. I've tried changing all the constructor parameters but none of them changed the problem.

I found a workaround that will make the factorial behave as intended. The trick is to add a second variable and make the factorial a pseudo unrary operator, so:
Instead of: 5!+5 I used: 5!(1)+5. This required no refactoring in my program, as the only change made was the way the operator was being inputted.
This way, the parser works wonderfully. You also need to change the actual Operator declaration to this:
Operator factorial = new Operator("!", 2, true, Operator.PRECEDENCE_POWER + 1) {
#Override public double apply(double... args) {
final long arg = (long) args[0];
if ((double) arg != args[0]) {
throw new IllegalArgumentException("Operand for factorial has to be an integer");
}
if (arg < 0) {
throw new IllegalArgumentException("The operand of the factorial can not " +
"be " +
"less than zero");
}
double result = 1;
for (int i = 1; i <= arg; i++) {
result *= i;
}
return result;
}
};
I suppose one should avoid using unary operators altogether when using EXP4J.

It certainly has to do with there being two operators in a row.
Or, alternatively, it has to do with EXP4J being a poor implementation of an expression parser.
Anyway you look at it, there is no simple solution using EXP4J. It is simply not up to the task of handling postfix operators. (If you were prepared to use !5 to mean "5 factorial", there wouldn't be a problem; just make sure that you give it the same precedence as the other unary prefix operators. But the fact that you don't want to use a function suggests that you don't want to change the syntax of ! either.)
The big hint is that the Operator constructor does not have any way to specify whether an operator is prefix or postfix. (! is a postfix operator, because it follows its operand, unlike, say, unary - with is prefix.)
If the expression parser doesn't know, it certainly cannot validate the input, and there's good reason to believe that it can't accurately parse it either. [Note 1] Another hint is that custom operators need to be declared as taking 1 or 2 operands, which means that a custom operator cannot act like - which has both prefix and infix syntax.
The special-case hack for unary + and - operators interacts badly with the hack which doesn't attempt to check whether a unary operator is prefix or postfix. In 5!+5, the + immediately follows an operator, which EXP4J interprets as indicating that the + is a prefix unary +. So it parses that almost as if it were (5!)(+5) except that it does not insert the implicit multiplication that it would insert if you actually wrote (5!)(+5).
So you get the exception, because you can't have two value side-by-side without an operator between them.
Actually fixing this would require quite a bit of rewriting. You might well be better off using a different expression evaluator, or throwing your own together with a standard parser generator.

Related

Java Ternary operator outputs different result than if else statement

I'm writing a program that takes a number and removes the trailing zeros if the number is an integer.
I am using the ternary operator but it doesn't work as expected. But if I write it as a if else statement it works.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double number = scanner.nextDouble();
System.out.println(((int)number == (double)number) ? (int)number : number); // Always outputs a double
if ((int)number == (double)number) { // Outputs correct result
System.out.println((int)number);
}
else {
System.out.println(number);
}
}
}
For example if I input 5 i get
5.0
5
if I input 7.3 I get
7.3
7.3
So it seems that it works for the if else statement but not the ternary operator.
In your if/else statement, you're calling PrintStream.println(int) or PrintStream.println(double) depending on which branch you're taking.
With the conditional operator ?: you're always calling PrintStream.println(double), because that's the type of the ?: expression. When the second and third operands of the ?: operator have different types, the compiler picks the overall type of the expression according to the rules of JLS 15.25 and performs appropriate conversions where necessary.
In this case, the overall type is double, so it's as if you're writing:
double tmp = ((int) number == (double)number) ? (int)number : number;
System.out.println(tmp);
This behavior is totally fine and caused by the type resolution.
The ternary operator itself has a type, that is inferred by the possible outcomes.
Since one outcome has type (int) and the other has type (double), the type of the ternary operator is always double.
The rest of the story should be clear. Printing a double to the console will always result in a decimal point representation.

Different result between normal conditional and ternary operation [duplicate]

I'm trying to write a method which gets a double, verifies if the number has something after the dot and if it does—returns a double, if doesn't—returns an int.
public class Solution {
public static void main(String[] args) {
double d = 3.000000000;
System.out.println(convert1(d));
System.out.println(convert2(d));
}
static Object convert1(double d) {
if(d % 1 == 0)
return (int) d;
else
return d;
}
static Object convert2(double d) {
return ((d%1) == 0) ? ((int) (d)) : d;
}
}
Output:
3
3.0
So, everything I want happens in method convert1(), but doesn't happen in method convert2(). It seems as these methods must do the same work. But what I have done wrong?
You're seeing an effect similar to the one in this question.
Slightly different rules govern the way Java handles types with the ternary operator than with an if statement.
Specifically, the standard says:
The type of a conditional expression is determined as follows:
...
Otherwise, if the second and third operands have types that are
convertible (§5.1.8) to numeric types, then there are several cases:
...
Otherwise, binary numeric promotion (§5.6.2) is applied to the
operand types, and the type of the conditional expression is the
promoted type of the second and third operands.
Flipping to that page of the standard, we see:
If either operand is of type double, the other is converted to double.
which is what's happening here, followed by autoboxing to a Double. It appears that no such conversion happens with the if statement, explaining the difference.
More broadly --- this isn't a very good idea. I don't think it's good design to return one of an int or a double depending on the value -- if you want to round something off, use Math.floor, and if you don't want decimals printed, use printf.
EDIT: I don't think it's a good idea to do hacky things to circumvent the regular numeric conversion system. Here's an idea that gives you a String directly, which appears to be what you want:
static String convert3(double d) {
return ((d % 1 == 0) ? Integer.toString((int)d) : Double.toString(d));
}
As the other answers have stated, this behavior is because both possible results of a ternary expression must have the same type.
Therefore, all you have to do to make your ternary version work the same way as convert1() is to cast the int to an Object:
static Object convert2(double d) {
return ((d % 1) == 0) ? ((Object) (int) (d)) : d;
}
The ternary operator requires both result values be the same type, so the int undergoes an automatic (safe) widening cast to double.
The ternary is not exactly the same as its if "equivalent".
To solve the problem with numbers after the dot:
public Object convert(double number){
double whole = Math.floor(number);
if(Math.abs(whole - number) < DELTA){
return (int) number;
}
return number;
}
The DELTA is sufficiently small constant, to solve the problem with integers encoded in floating point format.
I have written the code from memory, but I think the idea behind it is clear.

Difference between if-else and conditional-operator [duplicate]

I'm trying to write a method which gets a double, verifies if the number has something after the dot and if it does—returns a double, if doesn't—returns an int.
public class Solution {
public static void main(String[] args) {
double d = 3.000000000;
System.out.println(convert1(d));
System.out.println(convert2(d));
}
static Object convert1(double d) {
if(d % 1 == 0)
return (int) d;
else
return d;
}
static Object convert2(double d) {
return ((d%1) == 0) ? ((int) (d)) : d;
}
}
Output:
3
3.0
So, everything I want happens in method convert1(), but doesn't happen in method convert2(). It seems as these methods must do the same work. But what I have done wrong?
You're seeing an effect similar to the one in this question.
Slightly different rules govern the way Java handles types with the ternary operator than with an if statement.
Specifically, the standard says:
The type of a conditional expression is determined as follows:
...
Otherwise, if the second and third operands have types that are
convertible (§5.1.8) to numeric types, then there are several cases:
...
Otherwise, binary numeric promotion (§5.6.2) is applied to the
operand types, and the type of the conditional expression is the
promoted type of the second and third operands.
Flipping to that page of the standard, we see:
If either operand is of type double, the other is converted to double.
which is what's happening here, followed by autoboxing to a Double. It appears that no such conversion happens with the if statement, explaining the difference.
More broadly --- this isn't a very good idea. I don't think it's good design to return one of an int or a double depending on the value -- if you want to round something off, use Math.floor, and if you don't want decimals printed, use printf.
EDIT: I don't think it's a good idea to do hacky things to circumvent the regular numeric conversion system. Here's an idea that gives you a String directly, which appears to be what you want:
static String convert3(double d) {
return ((d % 1 == 0) ? Integer.toString((int)d) : Double.toString(d));
}
As the other answers have stated, this behavior is because both possible results of a ternary expression must have the same type.
Therefore, all you have to do to make your ternary version work the same way as convert1() is to cast the int to an Object:
static Object convert2(double d) {
return ((d % 1) == 0) ? ((Object) (int) (d)) : d;
}
The ternary operator requires both result values be the same type, so the int undergoes an automatic (safe) widening cast to double.
The ternary is not exactly the same as its if "equivalent".
To solve the problem with numbers after the dot:
public Object convert(double number){
double whole = Math.floor(number);
if(Math.abs(whole - number) < DELTA){
return (int) number;
}
return number;
}
The DELTA is sufficiently small constant, to solve the problem with integers encoded in floating point format.
I have written the code from memory, but I think the idea behind it is clear.

Why mixing + and cast does not produce an error in "+(int)+(long)-1"?

Why does this print 1?
import java.util.*;
import java.lang.*;
import java.io.*;
class Main
{
public static void main (String[] args) throws java.lang.Exception
{
// your code goes here
System.out.println((byte)+(short)-(int)+(long)-1);
}
}
Can we mix casting and +,- unary operators? I know that we can do casting multiple times, but why doesn't putting + ,- unary operators in between produce an error?
You are not adding nor substracting. Those + and - operators are unary sign operators.
See documentation at The Unary Operators section.
The sequence at stake:
(byte)+(short)-(int)+(long)-1
is evaluated from right to left like this:
the initial value is -1
casting to long (which is still -1)
unary + sign (still -1)
casting to int (still -1)
unary - sign (now the value is 1)
so on (the value remains 1 until the end)
These + and - are unary ones.
More specifically, it is in fact:
System.out.println((byte) (+((short) (-((int) (+((long) -1)))))));
if you remove all casting from your example, because in this case it will do nothing
System.out.println((byte)+(short)-(int)+(long)-1);
will become
System.out.println( + - + -1);
now you can see that just the operators are still there and because minus and minus are plus your result will be 1
basically you can think of it like:
var mylong = +(long)-1; <- -1
var myint = -(int)mylong; <- 1
var myshort = +(short)myint; <- 1
var mybyte = (byte)myshort; <- 1
I know we can do casting multiple times. But putting + ,- unary operators in between doesn't give error?
It is simply a consequence of the consistency of Java's grammar. When you write
+ 1
what you actually wrote is a unary numeric expression which decomposes into two parts:
the unary plus operator: +
a numeric expression, in this case the int literal 1.
Another example of a numeric expression is a cast expression (also unary):
(int) 1
Therefore you can substitute this for the original 1 above:
+ (int) 1
Repeating the same consistent process we can end up with a nested unary expression of arbitrary complexity. To return to your key question:
why?
Because Java would actually need a specific rule against such expressions.

Trying to do bitwise AND with two ints in Java, but the compiler tries to use them as bools

Here's a function I wrote in C++ (which works) and am now trying to convert to Java:
static String parseAInstruction(String line) {
int n = getValue(line);
if (n>=0) {
String inst = "0";
for (int i=14; i>=0; --i) {
if (Math.pow(2, i) & n)
inst += "1";
else
inst += "0";
}
return inst;
} else {
return "error";
}
}
This compiles just fine in C++, but when trying to compile in Java, I get the following error:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Type mismatch: cannot convert from int to boolean
at parser.Parser.parseAInstruction(Parser.java:38)
at parser.Parser.main(Parser.java:50)
Looking up bitwise AND in Java, it seems that it should be the same. It shouldn't have anything to do with booleans at all. Can anyone tell me the problem?
In Java you can only use boolean values in if clauses. So the result of a bitwise AND of two numbers is not valid in an if statement.
Try this instead: if ((Math.pow(2, i) & n) != 0) {.
[Edit] However, as commenter #jlordo points out, next you'll have to resolve the problem of doing a bitwise AND between a double and an integer.
The problem is that you can't just use an integer in the conditon expression for if. you need something like:
if (((int)Math.pow(2, i) & n) != 0)
Your problem is here:
if (Math.pow(2, i) & n)
In Java, you need to have a boolean value as a condition. You probably want:
if (((int)(Math.pow(2, i)) & n) != 0)
The cast to int is necessary because Math.pow() will return a double and the & operator is only defined for two int operands.
Trying to do bitwise AND with two ints in Java, but the compiler tries to use them as bools
No it doesn't. The result of the bitwise AND is of type int. Java requires the expression in an if statement to be of type boolean, and yours isn't. You need to convert the expression to boolean.
Math.pow in Java is calculated with a logarithm and an exponentiation (unlike C++, where the compiler often optimizes calls to pow), both very slow, approximate, floating point operations.
It doesn't even make sense to use here - you're working with bits, so work with bits:
(n & (1 << i)) != 0 is what you're looking for.

Categories

Resources