2015-07-31 3 views
3

По какой-то причине мне нужно сравнить два примитива: long и float.Как правильно сравнивать длинные и плавающие примитивы?

Могу ли я использовать следующий код для этого?

long a = 111L; 
float b = 111.1f 

if (a > b) { 
    ... 
} 

Я знаю, что поплавок и поплавок можно сравнить с некоторой точностью с использованием значения эпсилон и т.д.

Но как я могу выполнить сравнение для моего случая более правильно?

Спасибо всем.

+0

Я дал ответ [здесь] (HTTP: // stackoverflow.com/questions/30628945/codeblocks-c-bug), которые могут вам помочь. –

+2

check out this http://stackoverflow.com/questions/7392167/comparing-float-and-double-primitives-in-java –

+0

Как вы определяете «правильно»? В приведенном выше коде вам будет сказано, что '111' * не * больше, чем' 111.1f', что я называю «правильным» для себя. Поэтому я не вижу причин делать это по-другому ... – Holger

ответ

3

Вы можете обернуть их обоих в BigDecimal и их сравнить их:

long a = 111L; 
float b = 111.1f; 
BigDecimal first = new BigDecimal(a); 
BigDecimal second = new BigDecimal(b, MathContext.DECIMAL32); 
if (first.compareTo(second) > 0) { ... } 

Для того, чтобы понять, почему мы заинтересованы иметь как операндов под того же типа, давайте копать немного в JLS 5.6.2 Binary Numeric Promotion:

Когда оператор применяет двоичного цифрового продвижение к паре операндов, каждый из которых должен обозначать значение, которое является обратимым в NUM тип Эрика, применяются следующие правила, в следующем порядке:

  1. Если какие-либо из операндов имеют ссылочный типа, он подвергается распаковке преобразования (§5.1.8).

  2. примитивные преобразования Добавления новое (§5.1.2) применяются для преобразования либо или оба операнда, как указано по следующим правилам:

    • Если один из операндов имеют типа double, другой преобразуются в double.

    • В противном случае, если один из операндов имеет тип float, другой преобразуется в float.

    • В противном случае, если один из операндов имеет тип long, другой преобразуется в long.

    • В противном случае оба операнда преобразуются в тип int.

С этим можно сделать вывод, что для сравнения a > blong операнд будет неявно повышен до float.Это, однако, может в конечном итоге с потерей точности, как указано в JLS 5.1.2 Widening primitive conversion:

расширяющейся Превращение int или значение long для float или длинного значения в два раза, может привести к потере точность - то есть результат может потерять часть наименее значимых бит значения. В этом случае результирующее значение с плавающей запятой будет правильно округленной версией целочисленного значения с использованием режима IEEE 754, близкого к ближайшему, (§4.2.4).

+1

Возможно, вам понадобится соответствующее округление с использованием 'float'. 'new BigDecimal (b, MathContext.DECIMAL32);' –

+0

Гораздо лучше ;-) – Holger

1

Если вы хотите использовать эпсилон вы можете сделать

// allow for the smallest rounding error 
if (a > b + Math.sign(b) * Float.ulp(b)) 

или

// assume six digits of precision 
static final double ERR = 1e-6; 

// allow for the smallest rounding error 
if (a > b + Math.sign(b) * b * ERR) 

Вы можете оставить Math.sign (б), если можно предположить, неотрицательные числа.

Однако использование BigDecimal может быть более ясным в этом случае, см. Ответ @ kocko.

BTW: Простейшее изменение, которое повысит вашу точность, заключается в использовании double вместо float. double буквально полмиллиарда раз более точен, и если у вас нет миллиарда из них, то извлеченная память, которую вы используете, не имеет значения.

+0

Различия в точности 'float' и' double' вряд ли будут когда-либо актуальны при сравнении значения с 'long'. Важно то, что 'long' с большим абсолютным значением не может быть точно выражено в' float', однако, если исходное значение не существует в более высокой точности, преобразование его в 'double' не волшебным образом добавьте больше точности. Но я не вижу, как epsilon может улучшить сравнение '>' в любом случае. Использование epsilon имеет смысл для теста на равенство, но не для упорядочения ... – Holger

+0

@Holger Что вы сомневаетесь в сравнении смысла. Разумеется, a: 100> b: 10, даже если ошибка равна 1. Должна ли ошибка быть 0, чтобы вы могли сказать 100> 10 с ошибками? –

+0

Ну, давайте остановимся на примере 'b = 10'. Исходным кодом является 'a> b', таким образом эффективно выполняя' a> 10.0'. Теперь вы замените его на 'a> b + Math.signum (b) * Math.ulp (b)', что означает, что вы эффективно выполните 'a> 10.000000000000002'. Почему, по вашему мнению, 'a> 10.000000000000002' является улучшением по сравнению с' a> 10.0'? Не имеет значения, '' '' 'long' в любом случае, но если' a' было значением с плавающей запятой, то подразумевалось, что введение epsilon означает 'false', если' a' окажется '10.000000000000001'. Не вижу улучшения здесь. – Holger

0

Сравнение longs и floats напрямую поддерживается Java. Однако есть некоторые оговорки:

Если вы сравниваете длинный и плавающий, длинный преобразуется в поплавок, потому что float считается более широким, чем длинный. Однако это преобразование может потерять точность: длинное значение будет передано в ближайшее значение float; поэтому для длинного a и поплавок b, a > b может быть ложным, хотя a больше b, если b - фактически значение поплавка, ближайшее к b.

Каждый длинный и поплавок значение может быть представлено BigDecimal, так что вы можете также использовать BigDecimal класс для сравнения длинный в поплавок:

long a = ...; 
float b = ...; 
if (BigDecimal.valueOf(a).compareTo(BigDecimal.valueOf(b)) > 0) { 
    ... 
} 
Смежные вопросы