1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-09 01:29:31 +00:00

Tease floating-point numbers

This commit is contained in:
Karol Stasiak 2021-05-16 23:32:33 +02:00
parent 2065c3b4ac
commit ca35367974
2 changed files with 154 additions and 0 deletions

View File

@ -0,0 +1,120 @@
package millfork.node
import millfork.env.{Environment, Type}
import millfork.output.NoAlignment
import java.lang.Double.doubleToRawLongBits
import java.lang.Float.floatToRawIntBits
/**
* @author Karol Stasiak
*/
abstract class FloatFormat(val name: String, val hasInfinity: Boolean, val mantissaLength: Int, val exponentLength: Int) {
def format(f: Double): Option[List[Int]]
def toStruct(env: Environment): StructDefinitionStatement = {
val alignment = env.get[Type]("word").alignment
val size = format(0.0).size
StructDefinitionStatement("float." + name,
List.tabulate(size)(i => FieldDesc("byte", "b" + i, None)),
Some(if (size % 2 == 0) alignment else NoAlignment))
}
}
object IEEE64LE extends FloatFormat("ieee64le", true, 53, 10) {
override def format(f: Double): Option[List[Int]] = {
val l = doubleToRawLongBits(f)
Some(List(
l.>>(0).toInt.&(0xff),
l.>>(8).toInt.&(0xff),
l.>>(16).toInt.&(0xff),
l.>>(24).toInt.&(0xff),
l.>>(32).toInt.&(0xff),
l.>>(40).toInt.&(0xff),
l.>>(48).toInt.&(0xff),
l.>>(56).toInt.&(0xff)
))
}
}
object IEEE64BE extends FloatFormat("ieee64be", true, 53, 10) {
override def format(f: Double): Option[List[Int]] = {
val l = doubleToRawLongBits(f)
Some(List(
l.>>(56).toInt.&(0xff),
l.>>(48).toInt.&(0xff),
l.>>(40).toInt.&(0xff),
l.>>(32).toInt.&(0xff),
l.>>(24).toInt.&(0xff),
l.>>(16).toInt.&(0xff),
l.>>(8).toInt.&(0xff),
l.>>(0).toInt.&(0xff)
))
}
}
object IEEE32LE extends FloatFormat("ieee32le", true, 53, 10) {
override def format(f: Double): Option[List[Int]] = {
val l = java.lang.Float.floatToIntBits(f.toFloat)
if (!l.isNaN && !l.isInfinite) {
if (l < Float.MinValue || l > Float.MaxValue) {
return None
}
}
Some(List(
l.>>(0).&(0xff),
l.>>(8).&(0xff),
l.>>(16).&(0xff),
l.>>(24).&(0xff)
))
}
}
object IEEE32BE extends FloatFormat("ieee32be", true, 53, 10) {
override def format(f: Double): Option[List[Int]] = {
val l = floatToRawIntBits(f.toFloat)
if (!l.isNaN && !l.isInfinite) {
if (l < Float.MinValue || l > Float.MaxValue) {
return None
}
}
Some(List(
l.>>(24).&(0xff),
l.>>(16).&(0xff),
l.>>(8).&(0xff),
l.>>(0).&(0xff)
))
}
}
object MBF extends FloatFormat("mbf", false, 31, 8) {
override def format(f: Double): Option[List[Int]] = {
val l = doubleToRawLongBits(f)
val sign = if (l < 0) 0x80 else 0
var mantissa = l.&(1L.<<(52) - 1).>>(21).toInt
if (l.&(1 << 20) != 0L) {
mantissa += 1
}
var exponent = l.>>>(52).&(0x7ff).toInt - 1023
if (mantissa < 0) {
mantissa -= Int.MinValue
exponent += 1
}
if (f != 0 && (exponent > 126 || exponent < -129)) return None
if (f == 0) return Some(List(0, sign,0,0,0));
Some(List(
exponent + 129,
sign ^ mantissa.>>(24).&(0xff),
mantissa.>>(16).&(0xff),
mantissa.>>(8).&(0xff),
mantissa.>>(0).&(0xff)
))
}
}

View File

@ -0,0 +1,34 @@
package millfork.test.auxilary
import org.scalatest.{FunSuite, Matchers}
import millfork.node.MBF
import java.lang.Double.{doubleToRawLongBits, longBitsToDouble}
/**
* @author Karol Stasiak
*/
class MBFSuite extends FunSuite with Matchers {
test("MBF test") {
MBF.format(10) should equal(Some(List(0x84, 0x20, 0, 0, 0)))
MBF.format(2) should equal(Some(List(0x82, 0, 0, 0, 0)))
MBF.format(1) should equal(Some(List(0x81, 0, 0, 0, 0)))
MBF.format(longBitsToDouble(doubleToRawLongBits(1) + 1)) should equal(Some(List(0x81, 0, 0, 0, 0)))
MBF.format(0) should equal(Some(List(0, 0, 0, 0, 0)))
MBF.format(0.5) should equal(Some(List(0x80, 0, 0, 0, 0)))
MBF.format(0.25) should equal(Some(List(0x7F, 0, 0, 0, 0)))
MBF.format(-0.5) should equal(Some(List(0x80, 0x80, 0, 0, 0)))
MBF.format(Double.MaxValue) should equal(None)
MBF.format(Double.NegativeInfinity) should equal(None)
MBF.format(Double.NaN) should equal(None)
MBF.format(.00014352314036) should equal(Some(List(0x74, 0x16, 0x7E, 0xB3, 0x1B)))
MBF.format(.0013422634824) should equal(Some(List(0x77, 0x2F, 0xEE, 0xE3, 0x85)))
MBF.format(.0096140170119) should equal(Some(List(0x7A, 0x1D, 0x84, 0x1C, 0x2A)))
MBF.format(.055505126860) should equal(Some(List(0x7C, 0x63, 0x59, 0x58, 0x0A)))
MBF.format(.24022638462) should equal(Some(List(0x7E, 0x75, 0xFD, 0xE7, 0xC6)))
MBF.format(.69314718608) should equal(Some(List(0x80, 0x31, 0x72, 0x18, 0x10)))
MBF.format(1 / Math.log(2)) should equal(Some(List(0x81, 0x38, 0xAA, 0x3B, 0x29)))
// 11.00 1001 0000 1111 1101 1010 1010 0010 0010 0001 01101000110000100011010011000100110001100110001010001011100000
MBF.format(Math.PI) should equal(Some(List(0x82, 0x49, 0x0F, 0xDA, 0xA2)))
}
}