diff --git a/src/main/scala/millfork/node/FloatFormat.scala b/src/main/scala/millfork/node/FloatFormat.scala new file mode 100644 index 00000000..442339ba --- /dev/null +++ b/src/main/scala/millfork/node/FloatFormat.scala @@ -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) + )) + } +} diff --git a/src/test/scala/millfork/test/auxilary/MBFSuite.scala b/src/test/scala/millfork/test/auxilary/MBFSuite.scala new file mode 100644 index 00000000..20c383d1 --- /dev/null +++ b/src/test/scala/millfork/test/auxilary/MBFSuite.scala @@ -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))) + } +}