Related
I have two decimal numbers in String form that are rounded slightly differently. I want a function that would treat them as "equal" if they only differ by 1 ulp (i.e. only the last digit differs by 1).
Currently the most readable form I can come up with is like:
private static boolean diffByUlp(String oldVal, String newVal) {
BigDecimal nb = new BigDecimal(newVal);
return nb.subtract(new BigDecimal(oldVal)).abs().equals(nb.ulp());
}
However, I'd really like a way to do this in one expression (so it fits in an if statement) and avoid using the expensive BigDecimals.
(BTW: they differ by more than 1 double (binary) ulp.)
Any suggestions?
I assume you are looking for a performance effective solution since you've already mentioned that using BigDecimal is too expensive in your case. Although giving advice on performance without knowing the whole context is quite tricky. You may consider a solution based on comparing characters from both decimal numbers stored as a String. It may give you a quick boost if numbers you compare are usually different starting from very first digits (e.g. comparing 120.0001 with 512.0 can be easily tracked just by comparing first character in both strings). But if for most cases your numbers are pretty close then you might stick to BigDecimal - it's all about measuring the performance with real data.
Below you can find an exemplary solution based on comparing characters from strings. It handles a case where two decimal numbers uses different precision. Also when comparing "1.00" with "1.00001" the first number is "treated" as "1.00000". You can use this class as a utility class that provides you a single static method that you can use in any if statement.
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
final class StringDecimal {
private static final Map<Integer, Integer> charToInt = new ConcurrentHashMap<>();
static {
charToInt.put(48, 0);
charToInt.put(49, 1);
charToInt.put(50, 2);
charToInt.put(51, 3);
charToInt.put(52, 4);
charToInt.put(53, 5);
charToInt.put(54, 6);
charToInt.put(55, 7);
charToInt.put(56, 8);
charToInt.put(57, 9);
}
private static boolean areEqual(String num1, String num2) {
int size = Math.min(num1.length(), num2.length()) - 1;
// 1. Compare first n-1 characters where n is max common length for both strings
for (int i = 0; i < size; i++) {
if (num1.charAt(i) != num2.charAt(i)) {
return false;
}
}
int lastDigitDiff = Math.max(num1.charAt(size), num2.charAt(size)) - Math.min(num1.charAt(size), num2.charAt(size));
// 2. Check last common digit
if (lastDigitDiff > 1) {
return false;
}
// 3. If both decimal numbers have same size, they are equal at this moment
if (num1.length() == num2.length()) {
return true;
}
if (num1.length() > num2.length()) {
return testRemainingDigits(num1, size);
}
return testRemainingDigits(num2, size);
}
private static boolean testRemainingDigits(String num, int size) {
int lastDigitsSum = 0;
int lastDigit = charToInt.getOrDefault((int) num.charAt(num.length() - 1), 0);
// 1. Check if last digit is equal to 1
if (lastDigit > 1) {
return false;
}
// 2. Sum all remaining digits from longer string and accept sum == 1
for (int i = num.length() - 1; i > size; i--) {
lastDigitsSum += charToInt.getOrDefault((int) num.charAt(i), 0);
}
return lastDigit == 0 && lastDigitsSum == 0 ||
lastDigit == 1 && lastDigitsSum == 1;
}
public static void main(String[] args) {
List<List<Object>> numbers = Arrays.asList(
Arrays.asList("1.00", "1.000000", true),
Arrays.asList("120.0", "121.0", false),
Arrays.asList("120.0", "120.1", true),
Arrays.asList("1024.00001", "1024.00000", true),
Arrays.asList("1024.00002", "1024.00000", false),
Arrays.asList("1024.00001", "1024.0000", true),
Arrays.asList("1024.00001", "1024", true),
Arrays.asList("1024.00010", "1024", false),
Arrays.asList("1024.00002", "1024", false),
Arrays.asList("1024.00001", "1025.00001", false)
);
for (List<Object> data : numbers) {
String num1 = (String) data.get(0);
String num2 = (String) data.get(1);
boolean expected = (boolean) data.get(2);
boolean result = areEqual(num1, num2);
String status = expected == result ? "OK" : "FAILED";
System.out.println("["+status+"] " + num1 + " == " + num2 + " ? " + result);
}
}
}
It's very imperative, but it's still quite easy to understand what happens under the hood. Complexity of this algorithm is O(n).
Running this exemplary program produces following output:
[OK] 1.00 == 1.000000 ? true
[OK] 120.0 == 121.0 ? false
[OK] 120.0 == 120.1 ? true
[OK] 1024.00001 == 1024.00000 ? true
[OK] 1024.00002 == 1024.00000 ? false
[OK] 1024.00001 == 1024.0000 ? true
[OK] 1024.00001 == 1024 ? true
[OK] 1024.00010 == 1024 ? false
[OK] 1024.00002 == 1024 ? false
[OK] 1024.00001 == 1025.00001 ? false
I hope it will help you coming up with the best solution to your problem.
You have high expectations in a very "floating" area.
Still one, not so serious, answer:
static boolean probablySame(String x, String y) {
return Math.abs(x.hashCode() - y.hashCode()) <= 1;
}
So you want to compactly check if two decimal values only differ by at most 1. For example 3.2 and 2.4 (difference is 0.8).
First you should note that the only purpose of BigDecimal is to provide infinite space and precision in contrast to the limited datatype double (same holds for BigInteger and int). However you only use it to parse a decimal value from a String. Using that class only for that purpose is quite a big overhead, as you already mentioned. Parsing values can also be done with the Double#parseDouble method (documentation), it returns a compact small double value.
All in all your code could look like this:
private static boolean differAtMostByOne(final String oldVal, final String newVal) {
final double oldValAsDouble = Double.parseDouble(oldVal);
final double newValAsDouble = Double.parseDouble(newVal),
final double difference = Math.abs(oldValAsDouble - newValAsDouble);
final double compareTo = 1.0;
final double precision = 0.000001;
final boolean differByAtMostOne = difference <= compareTo + precision;
return differByAtMostOne;
}
Or the same compact:
private static boolean differAtMostByOne(final String oldVal, final String newVal) {
return Math.abs(Double.parseDouble(oldVal) - Double.parseDouble(newVal)) < 1.000001;
}
Note that a direct comparison with the value 1.0 should be avoided when comparing with decimal values. Instead you should allow a small region around the value to account for precision loss.
Else it could be that you input values whose difference is exactly 1 but the computer may represent it by a value like 1.000000000000000001 and the program should also accept it thus the precision region.
Assuming you need this only for Strings with the same lenght. Following might be a possible solution.
check that the strings are equal, except the last digit
check that the last digit is not more of then by one
The snippet should only demonstrate the principal. Further optimization possible.
static boolean diffByUlp(String s1, String s2) {
for (int i = 0; i < s1.length() - 1; i++) {
if (s1.charAt(i) != s2.charAt(i)) {
return false;
}
}
char c1 = s1.charAt(s1.length() - 1);
char c2 = s2.charAt(s2.length() - 1);
if (c1 >= c2) {
return c1-c2 <= 1;
}
return c2-c1 <= 1;
}
I was solving this problem, (41 from Project Euler), where I noticed that contains method of HashSet is working differently for Long as compared to Integer (I might be wrong here, please correct me if I am).
The question is -
We shall say that an n-digit number is pandigital if it makes use of
all the digits 1 to n exactly once. For example, 2143 is a 4-digit
pandigital and is also prime.
What is the largest n-digit pandigital prime that exists?
My code for checking whether the number is Pandigital or not is -
private static boolean isPan(Long n) {
HashSet<Long> list = new HashSet<Long>();
int count = 0;
while(n != 0){
list.add(n%10);
count++;
n /= 10;
}
for(int i = 9; i>count; i--){
if(list.contains(i)) return false;
}
for(int i = 1; i<= count; i++){
if(!list.contains(i)) return false;
}
return true;
}
This code gave me an infinite loop. So, I changed my code like this -
private static boolean isPan(Long n) {
HashSet<Integer> list = new HashSet<Integer>();
int count = 0;
while(n != 0){
list.add((int) (n%10));
count++;
n /= 10;
}
for(int i = 9; i>count; i--){
if(list.contains(i)) return false;
}
for(int i = 1; i<= count; i++){
if(!list.contains(i)) return false;
}
return true;
}
I just changed, HashSet<Long> to HashSet<Integer> and list.add(n%10) to list.add((int) n%10).
This gave me the correct answer, 7652413. So, can anyone explain why the contains method works differently for Long when compared to Integer?
contains(Object o) method doesn't work different for Long vs Integer. It works exactly the same, i.e.
Returns true if this set contains the specified element. More formally, returns true if and only if this set contains an element e such that (o==null ? e==null : o.equals(e)).
Notice however that method accepts Object as parameter type, not E. That means you can call it with any type of object. Of course, any object type other than E would cause it to return false, since equals() would fail for objects of different types (with some exceptions).
So, when you call contains(x), and x is a primitive, it will be auto-boxed, based on the type of x, not on the type of E. So if x is an int and E is Long, it'll always return false.
It is not contains() that suddenly works different, when you change Long to Integer. It is your code that works different, by correctly matching the type of value passed to contains() to the type of elements in the collection.
UPDATE
Your code is not very efficient:
It takes a Long as a parameter, but max n is by nature 9, and and int can store 9-digit numbers without overflow, so use of Long, and use of boxing, is unnecessary.
It allocates a new HashSet for every value being checked, and autoboxes every digit found, plus 9 times for the contains() calls.
Instead, this can be done using bit-manipulation, since as 32-bit int value can easily store 10 boolean values (flags) indicating whether a digit was present.
The code below will establish two bit-masks, found and expected, which will indicate whether a digit is found, and whether a digit was supposed to be found. Since solution should only use digits 1-n, we'll claim digit 0 is present and expected (makes logic simpler, not having to do special checks for 0).
If a digit is presented twice (or digit 0 is presented once), another expected digit will be missing, and found will not equal expected.
private static boolean isPandigital(int number) {
int found = 1, expected = 1;
for (int n = number; n != 0; n /= 10, expected = (expected << 1) | 1)
found |= 1 << (n % 10);
return (found == expected);
}
If you want your code run correctly,see your code whose list is HashSet<Long>:
for(int i = 1; i<= count; i++){
if(!list.contains(i)) return false;
}
you can change the type of variable i to long,or change if(!list.contains(i)) return false; to the if(!list.contains(Long.valueOf(i))) return false;
Because of the contains will check element in existence by the element's method equals.In above code,the variable i is auto-boxed to Integer instance,because the variable i is primitive int.
And see the Integer equals:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
but your list element's type is Long,so the line if(!list.contains(i)) return false; will return false always.
The method cannot have if or loop. so it returns true if a number is contained in an interval. it works with [1,6]... but how would I fix this so it would work with an open (1,6) or an interval such as [1,6)??'
I want it to be able to return all scenarios with (1,3] (1,3) [1,3) and [1,3], but with no if statements. How could I make one return statement for to be able to return these scenarios
Remove equals sign (=). Like that:
return (n > leftNumber && n < rightNumber);
You can modify for the others situations (such as [1,6) and (1, 6[)
Change your return statement to look like below if you want to work for interval [1,6)
return (n >= leftNum && n < rightNum);
For (1,6), the return statement can be modified as below
return (n > leftNum && n < rightNum);
You'd need a class to define which type of interval you want on each side of the comparison. I would use an enum:
public enum Interval {
CLOSED, OPEN;
}
Then you would redefine the method declaration as:
public boolean contains(double number, Interval left, Interval right)
and redefine the return statement with ternary operators to return the desired boolean.
The only way I can think to do this given your restrictions is to add a level of indirection. Your contains method needs to take some sort of interval calculation:
boolean contains(double number, Interval interval) {
double leftNum = this.getLeftEndPoint();
double rightNum = this.getRightEndPoint();
return interval.check(leftNum, number, rightNum);
}
Then you create the four interval objects you need:
class ClosedInterval implements Interval {
#Override
boolean check(double lowerBound, double value, double upperBound) {
return (lowerBound <= value) && (value <= upperBound);
}
}
class LowerHalfOpenInterval implements Interval {
#Override
boolean check(double lowerBound, double value, double upperBound) {
return (lowerBound < value) && (value <= upperBound);
}
}
etc...
This seems needlessly complex for the problem at hand, though. You could also just define separate methods for each of the various contains variants you want to handle.
I need help sorting an integer array using selection sort. It won't sort for some reaons. Below is my demo/main.
02
20
01
it should be
01
02
20
My demo/main:
public static void main(String[] args) {
SelectionSortArray[] ints = new SelectionSortArray[3];
ints [0] = new SelectionSortArray(02);
ints [1] = new SelectionSortArray(20);
ints [2] = new SelectionSortArray(01);
System.out.println("Unsorted array: ");
for (int index = 0; index < ints.length; index++) {
System.out.println(ints[index]);
}
SelectionSort.selectionSort(ints);
System.out.println(" ");
System.out.println("Sorted array using selection sort: ");
for (int index = 0; index < ints.length; index++) {
System.out.println(ints[index]);
}
}
Your compareTo method in the SelectionSortArray class is incorrect. The compareTo method must return an int less than zero if the current object is less than the other object, yet you have it returning 1.
Quoting from the linked Javadocs:
Compares this object with the specified object for order. Returns a
negative integer, zero, or a positive integer as this object is less
than, equal to, or greater than the specified object.
Try these changes:
if (num == other.num) {
result = 0; // This was correct; no change here.
} else if (num < other.num) {
result = -1; // Changed from 1 to -1.
} else {
result = 1; // 1 or 2 is fine, as long as it's positive
}
Your CompareTo function is wrong. Just change it to:
public int compareTo(SelectionSortArray other) {
return num.compareTo(other.num);
}
The key is the -1 / 0 / 1 return codes, rather than the "0,1,2" that you're using.
Generally if you're comparing built in types, it's easier to just delegate to the built in comparison operators.
Note: To use "num.compareTo" you need to use "Integer" rather than "int". If you want to stick with "int", you need the solution posted by rgettman.
Note that along with the changes to compareTo,
return Integer.compare(this.num,other.num)
Which is implemented to return what you are returning but in a concise way
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
if you want to see the printed output in the way you listed in your question use
System.out.format("%02d%n",ints[index].num);
or alter toString() to return String.format("%02d",num)
You might also want to see java.util.Arrays source code so see how few other similar methods have been implemented. Also consider changing your class name to indicate that it holds number CustomNumber and encapsulate your class to avoid direct access to num.
And another Interesting catch in your code
SelectionSortArray[] ints = new SelectionSortArray[8];
ints[0] = new SelectionSortArray(01);
ints[1] = new SelectionSortArray(02);
ints[3] = new SelectionSortArray(03);
ints[4] = new SelectionSortArray(04);
ints[5] = new SelectionSortArray(05);
ints[6] = new SelectionSortArray(06);
ints[7] = new SelectionSortArray(07);
ints[8] = new SelectionSortArray(08);//???
if you precede a number with 0 it will be an Octal number and allowed digits are 0 - 7 hence you will see compilation error above. Also
System.out.println(0123);
System.out.println(0234);
will not print
0123
0234
as you (un)expected !
83
156
Just be cautious while using octal numbers in your code.
I was asked in an interview, how to determine whether a number is positive or negative. The rules are that we should not use relational operators such as <, and >, built in java functions (like substring, indexOf, charAt, and startsWith), no regex, or API's.
I did some homework on this and the code is given below, but it only works for integer type. But they asked me to write a generic code that works for float, double, and long.
// This might not be better way!!
S.O.P ((( number >> 31 ) & 1) == 1 ? "- ve number " : "+ve number );
any ideas from your side?
The integer cases are easy. The double case is trickier, until you remember about infinities.
Note: If you consider the double constants "part of the api", you can replace them with overflowing expressions like 1E308 * 2.
int sign(int i) {
if (i == 0) return 0;
if (i >> 31 != 0) return -1;
return +1;
}
int sign(long i) {
if (i == 0) return 0;
if (i >> 63 != 0) return -1;
return +1;
}
int sign(double f) {
if (f != f) throw new IllegalArgumentException("NaN");
if (f == 0) return 0;
f *= Double.POSITIVE_INFINITY;
if (f == Double.POSITIVE_INFINITY) return +1;
if (f == Double.NEGATIVE_INFINITY) return -1;
//this should never be reached, but I've been wrong before...
throw new IllegalArgumentException("Unfathomed double");
}
The following is a terrible approach that would get you fired at any job...
It depends on you getting a Stack Overflow Exception [or whatever Java calls it]... And it would only work for positive numbers that don't deviate from 0 like crazy.
Negative numbers are fine, since you would overflow to positive, and then get a stack overflow exception eventually [which would return false, or "yes, it is negative"]
Boolean isPositive<T>(T a)
{
if(a == 0) return true;
else
{
try
{
return isPositive(a-1);
}catch(StackOverflowException e)
{
return false; //It went way down there and eventually went kaboom
}
}
}
This will only works for everything except [0..2]
boolean isPositive = (n % (n - 1)) * n == n;
You can make a better solution like this (works except for [0..1])
boolean isPositive = ((n % (n - 0.5)) * n) / 0.5 == n;
You can get better precision by changing the 0.5 part with something like 2^m (m integer):
boolean isPositive = ((n % (n - 0.03125)) * n) / 0.03125 == n;
You can do something like this:
((long) (num * 1E308 * 1E308) >> 63) == 0 ? "+ve" : "-ve"
The main idea here is that we cast to a long and check the value of the most significant bit. As a double/float between -1 and 0 will round to zero when cast to a long, we multiply by large doubles so that a negative float/double will be less than -1. Two multiplications are required because of the existence of subnormals (it doesn't really need to be that big though).
What about this?
return ((num + "").charAt(0) == '-');
// Returns 0 if positive, nonzero if negative
public long sign(long value) {
return value & 0x8000000000000000L;
}
Call like:
long val1 = ...;
double val2 = ...;
float val3 = ...;
int val4 = ...;
sign((long) valN);
Casting from double / float / integer to long should preserve the sign, if not the actual value...
You say
we should not use conditional operators
But this is a trick requirement, because == is also a conditional operator. There is also one built into ? :, while, and for loops. So nearly everyone has failed to provide an answer meeting all the requirements.
The only way to build a solution without a conditional operator is to use lookup table vs one of a few other people's solutions that can be boiled down to 0/1 or a character, before a conditional is met.
Here are the answers that I think might work vs a lookup table:
Nabb
Steven Schlansker
Dennis Cheung
Gary Rowe
This solution uses modulus. And yes, it also works for 0.5 (tests are below, in the main method).
public class Num {
public static int sign(long x) {
if (x == 0L || x == 1L) return (int) x;
return x == Long.MIN_VALUE || x % (x - 1L) == x ? -1 : 1;
}
public static int sign(double x) {
if (x != x) throw new IllegalArgumentException("NaN");
if (x == 0.d || x == 1.d) return (int) x;
if (x == Double.POSITIVE_INFINITY) return 1;
if (x == Double.NEGATIVE_INFINITY) return -1;
return x % (x - 1.d) == x ? -1 : 1;
}
public static int sign(int x) {
return Num.sign((long)x);
}
public static int sign(float x) {
return Num.sign((double)x);
}
public static void main(String args[]) {
System.out.println(Num.sign(Integer.MAX_VALUE)); // 1
System.out.println(Num.sign(1)); // 1
System.out.println(Num.sign(0)); // 0
System.out.println(Num.sign(-1)); // -1
System.out.println(Num.sign(Integer.MIN_VALUE)); // -1
System.out.println(Num.sign(Long.MAX_VALUE)); // 1
System.out.println(Num.sign(1L)); // 1
System.out.println(Num.sign(0L)); // 0
System.out.println(Num.sign(-1L)); // -1
System.out.println(Num.sign(Long.MIN_VALUE)); // -1
System.out.println(Num.sign(Double.POSITIVE_INFINITY)); // 1
System.out.println(Num.sign(Double.MAX_VALUE)); // 1
System.out.println(Num.sign(0.5d)); // 1
System.out.println(Num.sign(0.d)); // 0
System.out.println(Num.sign(-0.5d)); // -1
System.out.println(Num.sign(Double.MIN_VALUE)); // -1
System.out.println(Num.sign(Double.NEGATIVE_INFINITY)); // -1
System.out.println(Num.sign(Float.POSITIVE_INFINITY)); // 1
System.out.println(Num.sign(Float.MAX_VALUE)); // 1
System.out.println(Num.sign(0.5f)); // 1
System.out.println(Num.sign(0.f)); // 0
System.out.println(Num.sign(-0.5f)); // -1
System.out.println(Num.sign(Float.MIN_VALUE)); // -1
System.out.println(Num.sign(Float.NEGATIVE_INFINITY)); // -1
System.out.println(Num.sign(Float.NaN)); // Throws an exception
}
}
This code covers all cases and types:
public static boolean isNegative(Number number) {
return (Double.doubleToLongBits(number.doubleValue()) & Long.MIN_VALUE) == Long.MIN_VALUE;
}
This method accepts any of the wrapper classes (Integer, Long, Float and Double) and thanks to auto-boxing any of the primitive numeric types (int, long, float and double) and simply checks it the high bit, which in all types is the sign bit, is set.
It returns true when passed any of:
any negative int/Integer
any negative long/Long
any negative float/Float
any negative double/Double
Double.NEGATIVE_INFINITY
Float.NEGATIVE_INFINITY
and false otherwise.
Untested, but illustrating my idea:
boolean IsNegative<T>(T v) {
return (v & ((T)-1));
}
It seems arbitrary to me because I don't know how you would get the number as any type, but what about checking Abs(number) != number? Maybe && number != 0
Integers are trivial; this you already know. The deep problem is how to deal with floating-point values. At that point, you've got to know a bit more about how floating point values actually work.
The key is Double.doubleToLongBits(), which lets you get at the IEEE representation of the number. (The method's really a direct cast under the hood, with a bit of magic for dealing with NaN values.) Once a double has been converted to a long, you can just use 0x8000000000000000L as a mask to select the sign bit; if zero, the value is positive, and if one, it's negative.
If it is a valid answer
boolean IsNegative(char[] v) throws NullPointerException, ArrayIndexOutOfBoundException
{
return v[0]=='-';
}
one more option I could think of
private static boolean isPositive(Object numberObject) {
Long number = Long.valueOf(numberObject.toString());
return Math.sqrt((number * number)) != number;
}
private static boolean isPositive(Object numberObject) {
Long number = Long.valueOf(numberObject.toString());
long signedLeftShifteredNumber = number << 1; // Signed left shift
long unsignedRightShifterNumber = signedLeftShifteredNumber >>> 1; // Unsigned right shift
return unsignedRightShifterNumber == number;
}
This one is roughly based on ItzWarty's answer, but it runs in logn time! Caveat: Only works for integers.
Boolean isPositive(int a)
{
if(a == -1) return false;
if(a == 0) return false;
if(a == 1) return true;
return isPositive(a/2);
}
I think there is a very simple solution:
public boolean isPositive(int|float|double|long i){
return (((i-i)==0)? true : false);
}
tell me if I'm wrong!
Try this without the code: (x-SQRT(x^2))/(2*x)
Write it using the conditional then take a look at the assembly code generated.
Why not get the square root of the number? If its negative - java will throw an error and we will handle it.
try {
d = Math.sqrt(THE_NUMBER);
}
catch ( ArithmeticException e ) {
console.putln("Number is negative.");
}
I don't know how exactly Java coerces numeric values, but the answer is pretty simple, if put in pseudocode (I leave the details to you):
sign(x) := (x == 0) ? 0 : (x/x)
If you are allowed to use "==" as seems to be the case, you can do something like that taking advantage of the fact that an exception will be raised if an array index is out of bounds. The code is for double, but you can cast any numeric type to a double (here the eventual loss of precision would not be important at all).
I have added comments to explain the process (bring the value in ]-2.0; -1.0] union [1.0; 2.0[) and a small test driver as well.
class T {
public static boolean positive(double f)
{
final boolean pos0[] = {true};
final boolean posn[] = {false, true};
if (f == 0.0)
return true;
while (true) {
// If f is in ]-1.0; 1.0[, multiply it by 2 and restart.
try {
if (pos0[(int) f]) {
f *= 2.0;
continue;
}
} catch (Exception e) {
}
// If f is in ]-2.0; -1.0] U [1.0; 2.0[, return the proper answer.
try {
return posn[(int) ((f+1.5)/2)];
} catch (Exception e) {
}
// f is outside ]-2.0; 2.0[, divide by 2 and restart.
f /= 2.0;
}
}
static void check(double f)
{
System.out.println(f + " -> " + positive(f));
}
public static void main(String args[])
{
for (double i = -10.0; i <= 10.0; i++)
check(i);
check(-1e24);
check(-1e-24);
check(1e-24);
check(1e24);
}
The output is:
-10.0 -> false
-9.0 -> false
-8.0 -> false
-7.0 -> false
-6.0 -> false
-5.0 -> false
-4.0 -> false
-3.0 -> false
-2.0 -> false
-1.0 -> false
0.0 -> true
1.0 -> true
2.0 -> true
3.0 -> true
4.0 -> true
5.0 -> true
6.0 -> true
7.0 -> true
8.0 -> true
9.0 -> true
10.0 -> true
-1.0E24 -> false
-1.0E-24 -> false
1.0E-24 -> true
1.0E24 -> true
Well, taking advantage of casting (since we don't care what the actual value is) perhaps the following would work. Bear in mind that the actual implementations do not violate the API rules. I've edited this to make the method names a bit more obvious and in light of #chris' comment about the {-1,+1} problem domain. Essentially, this problem does not appear to solvable without recourse to API methods within Float or Double that reference the native bit structure of the float and double primitives.
As everybody else has said: Stupid interview question. Grr.
public class SignDemo {
public static boolean isNegative(byte x) {
return (( x >> 7 ) & 1) == 1;
}
public static boolean isNegative(short x) {
return (( x >> 15 ) & 1) == 1;
}
public static boolean isNegative(int x) {
return (( x >> 31 ) & 1) == 1;
}
public static boolean isNegative(long x) {
return (( x >> 63 ) & 1) == 1;
}
public static boolean isNegative(float x) {
return isNegative((int)x);
}
public static boolean isNegative(double x) {
return isNegative((long)x);
}
public static void main(String[] args) {
// byte
System.out.printf("Byte %b%n",isNegative((byte)1));
System.out.printf("Byte %b%n",isNegative((byte)-1));
// short
System.out.printf("Short %b%n",isNegative((short)1));
System.out.printf("Short %b%n",isNegative((short)-1));
// int
System.out.printf("Int %b%n",isNegative(1));
System.out.printf("Int %b%n",isNegative(-1));
// long
System.out.printf("Long %b%n",isNegative(1L));
System.out.printf("Long %b%n",isNegative(-1L));
// float
System.out.printf("Float %b%n",isNegative(Float.MAX_VALUE));
System.out.printf("Float %b%n",isNegative(Float.NEGATIVE_INFINITY));
// double
System.out.printf("Double %b%n",isNegative(Double.MAX_VALUE));
System.out.printf("Double %b%n",isNegative(Double.NEGATIVE_INFINITY));
// interesting cases
// This will fail because we can't get to the float bits without an API and
// casting will round to zero
System.out.printf("{-1,1} (fail) %b%n",isNegative(-0.5f));
}
}
This solution uses no conditional operators, but relies on catching two excpetions.
A division error equates to the number originally being "negative". Alternatively, the number will eventually fall off the planet and throw a StackOverFlow exception if it is positive.
public static boolean isPositive( f)
{
int x;
try {
x = 1/((int)f + 1);
return isPositive(x+1);
} catch (StackOverFlow Error e) {
return true;
} catch (Zero Division Error e) {
return false;
}
}
What about the following?
T sign(T x) {
if(x==0) return 0;
return x/Math.abs(x);
}
Should work for every type T...
Alternatively, one can define abs(x) as Math.sqrt(x*x),
and if that is also cheating, implement your own square root function...
if (((Double)calcYourDouble()).toString().contains("-"))
doThis();
else doThat();
Combined generics with double API. Guess it's a bit of cheating, but at least we need to write only one method:
static <T extends Number> boolean isNegative(T number)
{
return ((number.doubleValue() * Double.POSITIVE_INFINITY) == Double.NEGATIVE_INFINITY);
}
Two simple solutions. Works also for infinities and numbers -1 <= r <= 1
Will return "positive" for NaNs.
String positiveOrNegative(double number){
return (((int)(number/0.0))>>31 == 0)? "positive" : "negative";
}
String positiveOrNegative(double number){
return (number==0 || ((int)(number-1.0))>>31==0)? "positive" : "negative";
}
There is a function is the math library called signnum.
http://www.tutorialspoint.com/java/lang/math_signum_float.htm
http://www.tutorialspoint.com/java/lang/math_signum_double.htm
It's easy to do this like
private static boolean isNeg(T l) {
return (Math.abs(l-1)>Math.abs(l));
}
static boolean isNegative(double v) {
return new Double(v).toString().startsWith("-");
}