2

What would be a simple/elegant solution to check if a double value is only in the set of
{.0, .1, .. .9, 1.0} values.

Right now I am doing

setOf(.0, .1, .2, .3, .4, .5, .6, .7, .8, .9, 1.0)

and check if a Double value contains.

Is there a more simpler/elegant solution?

Dachstein
  • 3,994
  • 6
  • 34
  • 61

3 Answers3

1

You can make it with sequences.

fun contains(d: Double) = d in generateSequence(0.0) { it + 0.1 }.takeWhile { it <= 1.0 }

If you want to make it shorter, add step function like there is one for Int sequences.

infix fun ClosedRange<Double>.step(step: Double): Sequence<Double> = 
    generateSequence(start) { it + step }.takeWhile { it <= endInclusive }

fun contains(d: Double) = d in 0.0..1.0 step 0.1

Edit

As mentioned in comment, simple in doesn't work because of complex Double calculations. Therefore you can add your own checking function:

val acceptableAccuracy = 1e-15
infix fun Double.nearlyIn(sequence: Sequence<Double>) = 
    sequence.any { this in (it - acceptableAccuracy..it + acceptableAccuracy) }

Then you need few changes in the code above:

fun contains(d: Double) = d nearlyIn (0.0..1.0 step 0.1)
Ircover
  • 2,406
  • 2
  • 22
  • 42
1

Since you're only really worried about the tens position, I'd just shift it once and check for 0..10:

fun Double.isSpecial() = (this * 10.0) in (0..10).map(Int::toDouble)

Testing with play.kotlinlang.org:

fun main() {
  listOf(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0).forEach(::checkSpecial)
  listOf(0.01, 0.11, 0.22, 1.01).forEach(::checkSpecial)
}

fun checkSpecial(value: Double) {
  println("$value isSpecial = ${value.isSpecial()}")
}

Outputs:

0.0 isSpecial = true
0.1 isSpecial = true
0.2 isSpecial = true
0.3 isSpecial = true
0.4 isSpecial = true
0.5 isSpecial = true
0.6 isSpecial = true
0.7 isSpecial = true
0.8 isSpecial = true
0.9 isSpecial = true
1.0 isSpecial = true
0.01 isSpecial = false
0.11 isSpecial = false
0.22 isSpecial = false
1.01 isSpecial = false

If you're less worried about elegance and more about performance, you could just do:

fun Double.isSpecial() = when (this) {
    0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 -> true
    else -> false
}

Which avoids allocating any sets or ranges entirely. If the range isn't dynamic, I'd just go with this, personally.

Kevin Coppock
  • 133,643
  • 45
  • 263
  • 274
  • Interesting solutions. Thanks – Dachstein Jun 06 '19 at 16:32
  • 2
    Although this is a "correct" solution according to the OP's stated requirements, it leaves me feeling uncomfortable because it compares double values for equality. This is not comparing that a value is, say, exactly 0.3; it's in fact comparing that a value is exactly equal to the IEEE binary representation that the compiler creates when it encounters the literal value "0.3" in source code. They're two different things. I'd much rather compare with a given tolerance. – DodgyCodeException Jun 06 '19 at 17:26
  • That's a fair point. Guess it depends on how the values are being generated but comparing with a given tolerance would definitely be safer. – Kevin Coppock Jun 06 '19 at 19:53
0

This will do it if you want to check steps of 0.1 for any double. Multiply by 10, check if the result is an integer.

fun isSpecial(v:Double) : Boolean {
    val y = v*10
    return y == y.toInt().toDouble()
}

Unless you explicitly only want 0.0-1.0 ?

Roger Johansson
  • 22,764
  • 18
  • 97
  • 193