-
Notifications
You must be signed in to change notification settings - Fork 624
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix float bias towards high exponents (#3463) #3465
Conversation
@@ -68,7 +68,7 @@ fun Arb.Companion.numericFloat( | |||
min: Float = -Float.MAX_VALUE, | |||
max: Float = Float.MAX_VALUE | |||
): Arb<Float> = arbitrary((numericEdgeCases.filter { it in min..max } + listOf(min, max)).distinct(), FloatShrinker) { | |||
it.random.nextDouble(min.toDouble(), max.toDouble()).toFloat() | |||
Float.fromBits(it.random.nextInt(min.toInt(), max.toInt())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if that's right? This would transform the float into an int, i.e. 3.0f
would become 3
instead of 1077936128
.
In theory we probably want to get the floats in the bits representations instead. Floats have a Float.toBits()
extensions that should do that if I remember correctly. If that's right roundtripping that should work.
Float.fromBits(it.random.nextInt(min.toBits(), max.toBits()))
additionally, there's also another Arb.Companion.float
in this file that is still using random.nextDouble. That would need to have the same logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah thinking about this again.. i think we need to also think of the exponent because I'm not sure if currently it will generate the right float.. I wonder if there is a better way for us to do this. One thing that comes to mind would be to keep using double random, with some configurable probability of sampling from -minFloat and +minFloat if the arguments are in the range. Or perhaps utilising the edgecase random function to do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @myuwono I think this is the only way, I can't think of any elegant way of doing this except this
val sign = it.random.nextInt(0, 2)
val exponent = it.random.nextInt(0, 256)
val mantissa = it.random.nextBits(23)
val bits = (sign shl 31) or (exponent shl 23) or mantissa
Float.fromBits(bits)
I also found a new way to draw the graph better
test("draw distribution graph") {
fun exponent(value: Float): Int {
if (value == 0f) {
return Int.MIN_VALUE
}
val bits = java.lang.Float.floatToIntBits(value)
val exp = (bits shr 23) and 0xff
return exp - 127
}
val generatedFloat = Arb.Companion.numericFloat(Float.MIN_VALUE, Float.MAX_VALUE * 100)
.generate(RandomSource.seeded(1235), EdgeConfig(0.0))
.take(1_000_000)
.map { it.value}
.groupBy{ number ->
exponent(number)
}
.toSortedMap()
fun intToGraph(count: Int): String{
val stringNum = ceil(count.toDouble()/100)
var result = ""
(1..stringNum.toInt()).forEach {
result += '-'
}
return result
}
generatedFloat.forEach {
println("Exponent ${it.key} count ${intToGraph(it.value.count())} ")
}
}
@younghoonk17 can you fix the tests then we can merge. Thanks! |
fix for #3463