mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-22 16:31:02 +00:00
Tease floating-point numbers
This commit is contained in:
parent
2065c3b4ac
commit
ca35367974
120
src/main/scala/millfork/node/FloatFormat.scala
Normal file
120
src/main/scala/millfork/node/FloatFormat.scala
Normal 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)
|
||||
))
|
||||
}
|
||||
}
|
34
src/test/scala/millfork/test/auxilary/MBFSuite.scala
Normal file
34
src/test/scala/millfork/test/auxilary/MBFSuite.scala
Normal 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)))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user