package net.torvald.util import kotlin.experimental.or /** * https://stackoverflow.com/questions/6162651/half-precision-floating-point-in-java#6162687 * * Created by minjaesong on 2017-04-21. */ typealias Float16Bits = Short class Float16() { var bits = 0.toShort() private set constructor(fval: Float) : this() { fromFloat(fval) } fun toFloat() = Float16.toFloat(bits) fun fromFloat(fval: Float) { bits = Float16.fromFloat(fval) } operator fun times(other: Float) = fromFloat(this.toFloat() * other) operator fun times(other: Float16) = fromFloat(this.toFloat() * other.toFloat()) operator fun div(other: Float) = fromFloat(this.toFloat() / other) operator fun div(other: Float16) = fromFloat(this.toFloat() / other.toFloat()) // operators are stripped: you don't calculate from FP16; this is only for storing values // companion object { fun toFloat(hbits: Short): Float { val hbits = hbits.toInt().and(0xFFFF) var mant = hbits and 0x03ff // 10 bits mantissa var exp = hbits and 0x7c00 // 5 bits exponent if (exp == 0x7c00) // NaN/Inf exp = 0x3fc00 // -> NaN/Inf else if (exp != 0) // normalized value { exp += 0x1c000 // exp - 15 + 127 if (mant == 0 && exp > 0x1c400) // smooth transition return java.lang.Float.intBitsToFloat(hbits and 0x8000 shl 16 or (exp shl 13) or 0x3ff) } else if (mant != 0) // && exp==0 -> subnormal { exp = 0x1c400 // make it normal do { mant = mant shl 1 // mantissa * 2 exp -= 0x400 // decrease exp by 1 } while (mant and 0x400 == 0) // while not normal mant = mant and 0x3ff // discard subnormal bit } // else +/-0 -> +/-0 return java.lang.Float.intBitsToFloat(// combine all parts hbits and 0x8000 shl 16 or (exp or mant shl 13)) // value << ( 23 - 10 ) } fun fromFloat(fval: Float): Short { val fbits = java.lang.Float.floatToIntBits(fval) val sign = fbits.ushr(16).and(0x8000).toShort() // sign only var `val` = (fbits and 0x7fffffff) + 0x1000 // rounded value if (`val` >= 0x47800000) // might be or become NaN/Inf { // avoid Inf due to rounding if (fbits and 0x7fffffff >= 0x47800000) { // is or must become NaN/Inf if (`val` < 0x7f800000) // was value but too large return sign or 0x7c00 // make it +/-Inf return sign or 0x7c00 or // remains +/-Inf or NaN (fbits and 0x007fffff).ushr(13).toShort() // keep NaN (and Inf) bits } return sign or 0x7bff.toShort() // unrounded not quite Inf } if (`val` >= 0x38800000) // remains normalized value return sign or (`val` - 0x38000000).ushr(13).toShort() // exp - 127 + 15 if (`val` < 0x33000000) // too small for subnormal return sign // becomes +/-0 `val` = (fbits and 0x7fffffff).ushr(23) // tmp exp for subnormal calc return sign or ((fbits and 0x7fffff or 0x800000) // add subnormal bit + 0x800000.ushr(`val` - 102) // round depending on cut off ).ushr(126 - `val`) // div by 2^(1-(exp-127+15)) and >> 13 | exp=0 .toShort() } } }