Kotlin float / int косвенное сравнение - PullRequest
1 голос
/ 30 мая 2019

Для проекта я пытаюсь реализовать класс Bit, который выполняет все обычные вещи, которые должен делать немного.

Конструктор должен иметь возможность принимать любые Number и устанавливать внутреннее состояние Bit на 0, если задано значение 0, иначе установить его на 1.

Все работает для целочисленных входов, но когда я получаю float ... очевидно, 0.0 не равно 0.

class Bit(value: Number) {
    var value: Int = (value != 0).toInt() // I implemented Bool-to-Int myself
}

Теперь проблема:

println(Bit(0).value)   // Prints 0
println(Bit(0f).value)  // Prints 1

Я думал, что это какая-то ошибка аппроксимации, но когда я попытался print(0f != 0), я получил Operator '!=' cannot be applied to 'Float' and 'Int'.Однако при сравнении чисел через конструктор проблем не возникало.

Я чувствую, что есть какая-то скрытая магия приведения типов, о которой я не знаю, поэтому мой вопрос в основном "почему это не ведет себя так, как задумано, и чтобыло бы элегантным способом заставить его вести себя, как задумано? "

Ответы [ 2 ]

1 голос
/ 31 мая 2019

Это связано с тем, что представление значений упаковано, как указано в документах .

Как вы заметили, вы не можете сравнивать различные типы (когда Float и Int представлены изначально).Но вы этого не делаете, вместо этого вы используете тип Number.

Рассмотрим следующий пример:

println(0.0f == 0)  // error: Operator '==' cannot be applied to 'Float' and 'Int'
val n : Number = 0L  // this forces value to be boxed
println(n == 0)   // compiles and prints false

Сначала компилятор предупреждает вас о невозможности сравнения.Однако, если вы заключите числовое значение в объект, тогда можно использовать оператор equalsTo, как у вас.

Вы обнаружите, что он возвращает true тогда и только тогда, когда обе стороны имеют один и тот же тип и одно и то же значение.

Не хотите идти дождь на ваш парад, но правильный способреализация вашего класса объявляет отдельные конструкторы для каждого числового типа.Это не позволит компилятору упаковывать ваши значения и позволит вам правильно определить равенство:

class Bit constructor(value : Boolean) {
    val value = if(value) 1 else 0

    constructor(value : Int) : this(value == 0)
    constructor(value : Float) : this(value == 0f)
    constructor(value : Long) : this(value == 0L)
    // etc
}
1 голос
/ 31 мая 2019

Я не могу сказать вам точно , почему он не ведет себя так, как вы ожидаете, но я могу дать вам легкое решение!

Интерфейс Number не такой мощный, как вам хотелось бы, но главное, что дает , - это способ преобразования его значения в любой из стандартных числовых типов. Вы можете использовать это так:

class Bit(value: Number) {
    var value = if (value.toDouble() == 0.0) 0 else 1
}

Это будет работать для всех Number реализаций: Byte, Double, Float, Int, Long, Short, AtomicInteger, AtomicLong, BigDecimal, Striped64 и любые другие, которые вы пишете или импортируете.

(Что бы вы ни делали, я предлагаю вам также переопределить установщик value, чтобы удостовериться, что он содержит только 0 или 1.)

...