Skip to content
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

Closed

Conversation

younghoonk17
Copy link

fix for #3463

@@ -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()))
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Author

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())} ")
      }
   }

@sksamuel
Copy link
Member

@younghoonk17 can you fix the tests then we can merge. Thanks!

@younghoonk17 younghoonk17 deleted the issue/3463-bias-floats branch May 6, 2023 08:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants