programing

두 개의 일반 숫자 값 비교

coolbiz 2021. 1. 14. 22:59
반응형

두 개의 일반 숫자 값 비교


두 유형의 변수와 비교하고 싶습니다 T extends Number. 이제 두 변수 중 어느 것이 다른 변수보다 크거나 같은지 알고 싶습니다. 불행히도 정확한 유형은 아직 모르지만 java.lang.Number. 어떻게 할 수 있습니까?

편집 : TreeSet실제로 자연 순서로 작동하는 s를 사용하여 다른 해결 방법을 시도 했습니다 (물론 AtomicInteger 및 AtomicLong을 제외한 모든 하위 클래스 Number구현 Comparable). 따라서 중복 값을 잃게됩니다. Lists를 사용할 때 Collection.sort()바운드 불일치로 인해 내 목록을 수락하지 않습니다. 매우 불만족 스럽습니다.


작동하는 (그러나 부서지기 쉬운) 솔루션은 다음과 같습니다.

class NumberComparator implements Comparator<Number> {

    public int compare(Number a, Number b){
        return new BigDecimal(a.toString()).compareTo(new BigDecimal(b.toString()));
    }

}

그래도 여전히 훌륭하지는 않습니다. (표준 Java 클래스가 수행하지만 계약에서 요구하지 않는) toString파싱 ​​가능한 값 반환하기 때문 입니다.BigDecimalNumberNumber

편집, 7 년 후 : 주석에서 지적했듯이 toString고려해야 할 세 가지 특수 사례가 있습니다 (적어도?) .


이것은 Number를 확장하는 모든 클래스에서 작동해야하며 자신과 비교할 수 있습니다. & Comparable을 추가하면 모든 유형 검사를 제거 할 수 있으며 Sarmun 답변과 비교할 때 무료로 런타임 유형 검사 및 오류 발생을 제공합니다.

class NumberComparator<T extends Number & Comparable> implements Comparator<T> {

    public int compare( T a, T b ) throws ClassCastException {
        return a.compareTo( b );
    }
}

당신을 위해 일 수있는 하나의 해결책은하지 작업하는 것입니다 T extends Number하지만 함께 T extends Number & Comparable. 이 유형은 " 인터페이스를 모두T 구현하는 유형으로 만 설정할 수 있습니다 ."를 의미 합니다.

이를 통해 비교 가능한 모든 숫자로 작동하는 코드를 작성할 수 있습니다. 정적으로 입력되고 우아합니다.

이것은 BennyBoy가 제안하는 것과 동일한 솔루션이지만 비교기 클래스뿐만 아니라 모든 종류의 메서드와 함께 작동합니다.

public static <T extends Number & Comparable<T>> void compfunc(T n1, T n2) {
    if (n1.compareTo(n2) > 0) System.out.println("n1 is bigger");
}

public void test() {
    compfunc(2, 1); // Works with Integer.
    compfunc(2.0, 1.0); // And all other types that are subtypes of both Number and Comparable.
    compfunc(2, 1.0); // Compilation error! Different types.
    compfunc(new AtomicInteger(1), new AtomicInteger(2)); // Compilation error! Not subtype of Comparable
}

요구 한 후 비슷한 질문을 여기에 답변을 공부하고, 나는 다음과 함께했다. gustafc에서 제공하는 솔루션보다 더 효율적이고 강력하다고 생각합니다.

public int compare(Number x, Number y) {
    if(isSpecial(x) || isSpecial(y))
        return Double.compare(x.doubleValue(), y.doubleValue());
    else
        return toBigDecimal(x).compareTo(toBigDecimal(y));
}

private static boolean isSpecial(Number x) {
    boolean specialDouble = x instanceof Double
            && (Double.isNaN((Double) x) || Double.isInfinite((Double) x));
    boolean specialFloat = x instanceof Float
            && (Float.isNaN((Float) x) || Float.isInfinite((Float) x));
    return specialDouble || specialFloat;
}

private static BigDecimal toBigDecimal(Number number) {
    if(number instanceof BigDecimal)
        return (BigDecimal) number;
    if(number instanceof BigInteger)
        return new BigDecimal((BigInteger) number);
    if(number instanceof Byte || number instanceof Short
            || number instanceof Integer || number instanceof Long)
        return new BigDecimal(number.longValue());
    if(number instanceof Float || number instanceof Double)
        return new BigDecimal(number.doubleValue());

    try {
        return new BigDecimal(number.toString());
    } catch(final NumberFormatException e) {
        throw new RuntimeException("The given number (\"" + number + "\" of class " + number.getClass().getName() + ") does not have a parsable string representation", e);
    }
}

가장 "일반적인"Java 기본 숫자는 두 배이므로 간단히

a.doubleValue() > b.doubleValue()

대부분의 경우 충분해야하지만 ... 숫자를 double로 변환 할 때 여기에 미묘한 문제가 있습니다. 예를 들어 다음은 BigInteger로 가능합니다.

    BigInteger a = new BigInteger("9999999999999992");
    BigInteger b = new BigInteger("9999999999999991");
    System.out.println(a.doubleValue() > b.doubleValue());
    System.out.println(a.doubleValue() == b.doubleValue());

결과 :

false
true

매우 극단적 인 경우가 될 것으로 예상하지만 가능합니다. 그리고 아닙니다-일반적인 100 % 정확한 방법은 없습니다. 숫자 인터페이스에는 정보를 잃지 않고 완벽한 방식으로 숫자를 표현할 수있는 어떤 유형으로 변환하는 exactValue ()와 같은 메소드가 없습니다.

실제로 이러한 완벽한 숫자를 갖는 것은 일반적으로 불가능합니다. 예를 들어 유한 공간을 사용하는 산술을 사용하여 Pi를 나타내는 것은 불가능합니다.


이것은 Number를 확장하는 모든 클래스에서 작동해야하며 자신과 비교할 수 있습니다.

class NumberComparator<T extends Number> implements Comparator<T> {

    public int compare(T a, T b){
        if (a instanceof Comparable) 
            if (a.getClass().equals(b.getClass()))
                return ((Comparable<T>)a).compareTo(b);        
        throw new UnsupportedOperationException();
    }
}

if(yourNumber instanceof Double) {
    boolean greaterThanOtherNumber = yourNumber.doubleValue() > otherNumber.doubleValue();
    // [...]
}

참고 :instanceof 검사가 반드시 필요하지 않은이 - 당신이 그들을 비교하는 방법을 정확하게에 따라 달라집니다. 물론 .doubleValue()모든 Number는 여기에 나열된 방법을 제공해야하므로 항상 사용할 있습니다 .

편집 : 의견에 명시된 바와 같이 (항상) BigDecimal 및 친구를 확인해야합니다. 그러나 그들은 .compareTo()방법 을 제공합니다 .

if(yourNumber instanceof BigDecimal && otherNumber instanceof BigDecimal) { 
    boolean greaterThanOtherNumber = ((BigDecimal)yourNumber).compareTo((BigDecimal)otherNumber) > 0;
} 

단순히 Number's doubleValue()방법을 사용 하여 비교할 수 있습니다. 그러나 결과가 귀하의 요구에 충분히 정확하지 않을 수 있습니다.


What about this one? Definitely not nice, but it deals with all necessary cases mentioned.

public class SimpleNumberComparator implements Comparator<Number>
    {
        @Override
        public int compare(Number o1, Number o2)
        {
            if(o1 instanceof Short && o2 instanceof Short)
            {
                return ((Short) o1).compareTo((Short) o2);
            }
            else if(o1 instanceof Long && o2 instanceof Long)
            {
                return ((Long) o1).compareTo((Long) o2);
            }
            else if(o1 instanceof Integer && o2 instanceof Integer)
            {
                return ((Integer) o1).compareTo((Integer) o2);
            }
            else if(o1 instanceof Float && o2 instanceof Float)
            {
                return ((Float) o1).compareTo((Float) o2);
            }
            else if(o1 instanceof Double && o2 instanceof Double)
            {
                return ((Double) o1).compareTo((Double) o2);
            }
            else if(o1 instanceof Byte && o2 instanceof Byte)
            {
                return ((Byte) o1).compareTo((Byte) o2);
            }
            else if(o1 instanceof BigInteger && o2 instanceof BigInteger)
            {
                return ((BigInteger) o1).compareTo((BigInteger) o2);
            }
            else if(o1 instanceof BigDecimal && o2 instanceof BigDecimal)
            {
                return ((BigDecimal) o1).compareTo((BigDecimal) o2);
            }
            else
            {
                throw new RuntimeException("Ooopps!");
            }

        }

    }

Let's assume that you have some method like:

public <T extends Number> T max (T a, T b) {
   ...
   //return maximum of a and b
}

If you know that there are only integers, longs and doubles can be passed as parameters then you can change method signature to:

public <T extends Number> T max(double a, double b) {
   return (T)Math.max (a, b);
}

This will work for byte, short, integer, long and double.

If you presume that BigInteger's or BigDecimal's or mix of floats and doubles can be passed then you cannot create one common method to compare all these types of parameters.


If your Number instances are never Atomic (ie AtomicInteger) then you can do something like:

private Integer compare(Number n1, Number n2) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {

 Class<? extends Number> n1Class = n1.getClass();
 if (n1Class.isInstance(n2)) {
  Method compareTo = n1Class.getMethod("compareTo", n1Class);
  return (Integer) compareTo.invoke(n1, n2);
 }

 return -23;
}

This is since all non-Atomic Numbers implement Comparable

EDIT:

This is costly due to reflection: I know

EDIT 2:

This of course does not take of a case in which you want to compare decimals to ints or some such...

EDIT 3:

This assumes that there are no custom-defined descendants of Number that do not implement Comparable (thanks @DJClayworth)


System.out.println(new BigDecimal(0.1d).toPlainString());
System.out.println(BigDecimal.valueOf(0.1d).toPlainString());
System.out.println(BigDecimal.valueOf(0.1f).toPlainString());
System.out.println(Float.valueOf(0.1f).toString());
System.out.println(Float.valueOf(0.1f).doubleValue());

ReferenceURL : https://stackoverflow.com/questions/2683202/comparing-the-values-of-two-generic-numbers

반응형