NaN and are almost as ugly and undesirable as null, except that they don’t explicitly throw errors. Why isn’t there a syntax to require a finite float value?
Well, usability would be terrible. Having to check each float value to be finite would mean hundreds of unnecessary checks. I don’t want to know how complicated my code (which is quite math heavy) would be if I had to do all those checks. Also I guess performance would be much worse with all those extra checks (I don’t think they can be optimized away easily).
You can define a class that checks for this in its init block, and defines operators etc.
Something like
class FiniteNumberException(message: String) : RuntimeException(message)
data class FiniteFloat(val value: Float) : Comparable<Number>, Number() {
init{
if(value.isInfinite())
throw FiniteNumberException("Value $value is not finite")
if(value.isNaN())
throw FiniteNumberException("Value $value is NaN")
}
override fun toByte() = value.toByte()
override fun toChar() = value.toChar()
override fun toInt() = value.toInt()
override fun toDouble() = value.toDouble()
override fun toFloat() = value
override fun toLong() = value.toLong()
override fun toShort() = value.toShort()
operator fun plus(other: FiniteFloat) = FiniteFloat(value + other.value)
operator fun minus(other: FiniteFloat) = FiniteFloat(value - other.value)
operator fun times(other: FiniteFloat) = FiniteFloat(value * other.value)
operator fun div(other: FiniteFloat) = FiniteFloat(value / other.value)
operator fun rem(other: FiniteFloat) = FiniteFloat(value % other.value)
operator fun inc() = FiniteFloat(value + 1)
operator fun dec() = FiniteFloat(value - 1)
operator fun rangeTo(other: FiniteFloat) = value..other.value
override fun compareTo(other: Number) = value.compareTo(FiniteFloat(other.toFloat()).value)
operator fun plus(other: Number) = FiniteFloat(value + FiniteFloat(other.toFloat()).value)
operator fun minus(other: Number) = FiniteFloat(value - FiniteFloat(other.toFloat()).value)
operator fun times(other: Number) = FiniteFloat(value * FiniteFloat(other.toFloat()).value)
operator fun div(other: Number) = FiniteFloat(value / FiniteFloat(other.toFloat()).value)
operator fun rem(other: Number) = FiniteFloat(value % FiniteFloat(other.toFloat()).value)
operator fun rangeTo(other: Number) = value..FiniteFloat(other.toFloat()).value
}
fun Number.toFiniteFloatOrNull() = try{ FiniteFloat(this.toFloat()) } catch (fne: FiniteNumberException) { null }
fun Number.toFiniteFloat() = FiniteFloat(this.toFloat())
Then you can do things like (3 + 4).toFiniteFloat() + 4.5
.
That would/could be more efficient as an inline class.
Indeed it would… if inline classes could have init blocks. You could probably get around that by making the constructor internal and doing the checking in a creation method, but it wouldn’t be as clean.