New Decimal class, with fixed point and compile-time precision
This commit is contained in:
parent
1c7abed6e9
commit
48ee669458
@ -4,7 +4,7 @@ import mekanism.api.math.FloatingLong
|
|||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
|
|
||||||
private val LONG_OVERFLOW = BigInteger.valueOf(Long.MAX_VALUE) + BigInteger.valueOf(Long.MAX_VALUE) + BigInteger.TWO
|
private val LONG_OVERFLOW = (BigInteger.valueOf(Long.MAX_VALUE) + BigInteger.valueOf(Long.MAX_VALUE) + BigInteger.TWO) * Decimal.PRECISION_POW_BI
|
||||||
private val LONG_OVERFLOW1 = BigInteger.valueOf(Long.MAX_VALUE) + BigInteger.valueOf(Long.MAX_VALUE) + BigInteger.ONE
|
private val LONG_OVERFLOW1 = BigInteger.valueOf(Long.MAX_VALUE) + BigInteger.valueOf(Long.MAX_VALUE) + BigInteger.ONE
|
||||||
|
|
||||||
fun Decimal.toFloatingLong(): FloatingLong {
|
fun Decimal.toFloatingLong(): FloatingLong {
|
||||||
@ -17,14 +17,16 @@ fun Decimal.toFloatingLong(): FloatingLong {
|
|||||||
return FloatingLong.MAX_VALUE
|
return FloatingLong.MAX_VALUE
|
||||||
}
|
}
|
||||||
|
|
||||||
return FloatingLong.create(whole.toLong(), (decimal * 10_000.0).toInt().toShort())
|
return FloatingLong.create(whole.toLong(), (fractionalFloat * 10_000.0).toInt().toShort())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun FloatingLong.toDecimal(): Decimal {
|
fun FloatingLong.toDecimal(): Decimal {
|
||||||
|
var conv = BigInteger.valueOf(value) * Decimal.PRECISION_POW_BI + BigInteger.valueOf((decimal.toDouble() / 10_000.0 * Decimal.PRECISION_DOUBLE).toLong())
|
||||||
|
|
||||||
// Overflow
|
// Overflow
|
||||||
if (value < 0L) {
|
if (value < 0L) {
|
||||||
return Decimal(LONG_OVERFLOW + BigInteger.valueOf(value), decimal / 10_000.0)
|
conv += LONG_OVERFLOW
|
||||||
}
|
}
|
||||||
|
|
||||||
return Decimal(value, decimal / 10_000.0)
|
return Decimal.raw(conv)
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,14 +2,15 @@ package ru.dbotthepony.mc.otm.core.math
|
|||||||
|
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
inline val BigInteger.isZero get() = this == BigInteger.ZERO
|
inline val BigInteger.isZero get() = this.signum() == 0
|
||||||
inline val BigInteger.isPositive get() = this > BigInteger.ZERO
|
inline val BigInteger.isPositive get() = this.signum() > 0
|
||||||
inline val BigInteger.isNegative get() = this < BigInteger.ZERO
|
inline val BigInteger.isNegative get() = this.signum() < 0
|
||||||
|
|
||||||
inline val BigDecimal.isZero get() = this == BigDecimal.ZERO
|
inline val BigDecimal.isZero get() = this.signum() == 0
|
||||||
inline val BigDecimal.isPositive get() = this > BigDecimal.ZERO
|
inline val BigDecimal.isPositive get() = this.signum() > 0
|
||||||
inline val BigDecimal.isNegative get() = this < BigDecimal.ZERO
|
inline val BigDecimal.isNegative get() = this.signum() < 0
|
||||||
|
|
||||||
val BI_INT_MAX: BigInteger = BigInteger.valueOf(Int.MAX_VALUE.toLong())
|
val BI_INT_MAX: BigInteger = BigInteger.valueOf(Int.MAX_VALUE.toLong())
|
||||||
val BI_INT_MIN: BigInteger = BigInteger.valueOf(Int.MIN_VALUE.toLong())
|
val BI_INT_MIN: BigInteger = BigInteger.valueOf(Int.MIN_VALUE.toLong())
|
||||||
@ -21,6 +22,35 @@ val BI_FLOAT_MIN: BigInteger = BigDecimal(Float.MIN_VALUE.toString()).toBigInteg
|
|||||||
val BI_DOUBLE_MAX: BigInteger = BigDecimal(Double.MAX_VALUE.toString()).toBigInteger()
|
val BI_DOUBLE_MAX: BigInteger = BigDecimal(Double.MAX_VALUE.toString()).toBigInteger()
|
||||||
val BI_DOUBLE_MIN: BigInteger = BigDecimal(Double.MIN_VALUE.toString()).toBigInteger()
|
val BI_DOUBLE_MIN: BigInteger = BigDecimal(Double.MIN_VALUE.toString()).toBigInteger()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant value which represent edge of meaningful bits.
|
||||||
|
*
|
||||||
|
* Equals to 0.000000000001
|
||||||
|
*/
|
||||||
|
const val EPSILON = 0.000000000001
|
||||||
|
|
||||||
|
fun weakEqualDoubles(a: Double, b: Double): Boolean {
|
||||||
|
if (a == b)
|
||||||
|
return true
|
||||||
|
|
||||||
|
return (a - b).absoluteValue <= EPSILON
|
||||||
|
}
|
||||||
|
|
||||||
|
fun weakCompareDoubles(a: Double, b: Double): Int {
|
||||||
|
if (weakEqualDoubles(a, b))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if (a > b)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun weakLessThan(a: Double, b: Double) = weakCompareDoubles(a, b) < 0
|
||||||
|
fun weakGreaterThan(a: Double, b: Double) = weakCompareDoubles(a, b) > 0
|
||||||
|
fun weakLessOrEqual(a: Double, b: Double) = weakCompareDoubles(a, b) <= 0
|
||||||
|
fun weakGreaterOrEqual(a: Double, b: Double) = weakCompareDoubles(a, b) >= 0
|
||||||
|
|
||||||
fun BigInteger.toIntSafe(): Int {
|
fun BigInteger.toIntSafe(): Int {
|
||||||
if (this > BI_INT_MAX) {
|
if (this > BI_INT_MAX) {
|
||||||
return Int.MAX_VALUE
|
return Int.MAX_VALUE
|
||||||
|
@ -11,46 +11,135 @@ import java.io.ObjectOutputStream
|
|||||||
|
|
||||||
object DecimalTests {
|
object DecimalTests {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ImpreciseFraction comparison")
|
@DisplayName("Decimal comparison")
|
||||||
fun comparison() {
|
fun comparison() {
|
||||||
check(Decimal(642, 0.43774) > Decimal(641, 0.43774)) { "must be bigger" }
|
check(Decimal(642.43774) > Decimal(641.43774)) { "must be bigger" }
|
||||||
check(Decimal(642, 0.43774) > Decimal(-641, 0.43774)) { "must be bigger" }
|
check(Decimal(642.43774) > Decimal(-641.43774)) { "must be bigger" }
|
||||||
check(Decimal(0) == Decimal(0)) { "integer notation" }
|
check(Decimal(0) == Decimal(0)) { "integer notation" }
|
||||||
check(Decimal(0.1) > Decimal(0)) { "must be bigger" }
|
check(Decimal(0.1) > Decimal(0)) { "must be bigger" }
|
||||||
check(Decimal(-0.1) < Decimal(0)) { "must be lesser" }
|
check(Decimal(-0.1) < Decimal(0)) { "must be lesser" }
|
||||||
check(Decimal(-1, -0.1) < Decimal(-1)) { "must be lesser" }
|
check(Decimal(-1.1) < Decimal(-1)) { "must be lesser" }
|
||||||
check(Decimal(0.1) == Decimal(0.1)) { "double notation" }
|
check(Decimal(0.1) == Decimal(0.1)) { "double notation" }
|
||||||
check(Decimal("0.1") == Decimal("0.1")) { "string notation" }
|
check(Decimal("0.1") == Decimal("0.1")) { "string notation" }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ImpreciseFraction mathematical operations")
|
@DisplayName("Decimal mathematical operations")
|
||||||
fun math() {
|
fun math() {
|
||||||
check((Decimal(1) + Decimal(2)) == Decimal(3)) { "1 + 2 != 3" }
|
assertEquals(Decimal(3), Decimal(1) + Decimal(2))
|
||||||
check((Decimal(0, 0.5) + Decimal(0, 0.5)) == Decimal(1)) { "0.5 + 0.5 != 1" }
|
assertEquals(Decimal(5), Decimal(2) + Decimal(3))
|
||||||
check((Decimal(0, 0.5) + Decimal(0, -0.5)) == Decimal(0)) { "0.5 + -0.5 != 1" }
|
assertEquals(Decimal(-1), Decimal(6) + Decimal(-7))
|
||||||
check((Decimal(0, 0.5) - Decimal(0, -0.5)) == Decimal(1)) { "0.5 - -0.5 != 1" }
|
assertEquals(Decimal(-1), Decimal(6) - Decimal(7))
|
||||||
check((Decimal(0, 0.3) - Decimal(1, 0.2)) == Decimal(0, -0.9)) { "0.3 - 1.2 != -0.9 ${Decimal(0, -0.9)} ${Decimal(0, 0.3) - Decimal(1, 0.2)}" }
|
assertEquals(Decimal(2), Decimal(1) * Decimal(2))
|
||||||
check((Decimal(0, 0.3) * Decimal(0, 0.3)) == Decimal(0, 0.09)) { "0.3 * 0.3 != 0.9 ${Decimal(0, 0.3) * Decimal(0, 0.3)}" }
|
assertEquals(Decimal(4), Decimal(2) * Decimal(2))
|
||||||
check((Decimal(2, 0.3) * Decimal(2, 0.3)) == Decimal(5, 0.29)) { "2.3 * 2.3 != 5.29 ${Decimal(2, 0.3) * Decimal(2, 0.3)}" }
|
assertEquals(Decimal(16), Decimal(4) * Decimal(4))
|
||||||
check((Decimal(4) / Decimal(2)) == Decimal(2)) { "4 / 2 != 2" }
|
assertEquals(Decimal(100_000), Decimal(100) * Decimal(1_000))
|
||||||
check((Decimal(6) / Decimal(2)) == Decimal(3)) { "6 / 2 != 2" }
|
assertEquals(Decimal(4), Decimal(16) / Decimal(4))
|
||||||
check((Decimal(1) / Decimal(0, 0.25)) == Decimal(4)) { "1 / 0.25 != 4" }
|
assertEquals(Decimal(3), Decimal(12) / Decimal(4))
|
||||||
|
assertEquals(Decimal(1), Decimal(15) / Decimal(15))
|
||||||
|
|
||||||
|
assertEquals(Decimal("0.1"), Decimal(1) / Decimal(10))
|
||||||
|
assertEquals(Decimal("-0.1"), Decimal(1) / Decimal(-10))
|
||||||
|
assertEquals(Decimal("-0.1"), Decimal(-1) / Decimal(10))
|
||||||
|
assertEquals(Decimal("0.1"), Decimal(-1) / Decimal(-10))
|
||||||
|
assertEquals(Decimal("0.5"), Decimal("0.25") + Decimal("0.25"))
|
||||||
|
assertEquals(Decimal("0.5"), Decimal("0.25") * Decimal(2))
|
||||||
|
assertEquals(Decimal("-0.5"), Decimal("0.25") * Decimal(-2))
|
||||||
|
assertEquals(Decimal("-0.5"), Decimal("-0.25") * Decimal(2))
|
||||||
|
assertEquals(Decimal("0.3"), Decimal(0.2) + Decimal(0.1))
|
||||||
|
assertEquals(Decimal("0.3"), Decimal("0.2") + Decimal("0.1"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ImpreciseFraction store/load")
|
@DisplayName("Decimal precision test")
|
||||||
|
fun precisionTest() {
|
||||||
|
assertEquals(0.0, Decimal(4).fractionalDouble)
|
||||||
|
assertEquals(0.2, Decimal(4.2).fractionalDouble)
|
||||||
|
assertEquals(0.2, Decimal("4.2").fractionalDouble)
|
||||||
|
assertEquals(0.25, Decimal("4.25").fractionalDouble)
|
||||||
|
assertEquals(-0.25, Decimal("-1.25").fractionalDouble)
|
||||||
|
assertEquals(-0.2, Decimal("-1.2").fractionalDouble)
|
||||||
|
assertEquals(0.0, Decimal("-1").fractionalDouble)
|
||||||
|
|
||||||
|
assertEquals(0.0f, Decimal(4).fractionalFloat)
|
||||||
|
assertEquals(0.2f, Decimal(4.2).fractionalFloat)
|
||||||
|
assertEquals(0.2f, Decimal("4.2").fractionalFloat)
|
||||||
|
assertEquals(0.25f, Decimal("4.25").fractionalFloat)
|
||||||
|
assertEquals(-0.25f, Decimal("-1.25").fractionalFloat)
|
||||||
|
assertEquals(-0.2f, Decimal("-1.2").fractionalFloat)
|
||||||
|
assertEquals(0.0f, Decimal("-1").fractionalFloat)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Decimal toString() tests")
|
||||||
|
fun toStringTest() {
|
||||||
|
assertEquals("0.0", Decimal(0).toString())
|
||||||
|
assertEquals("0.0", Decimal("0.000").toString())
|
||||||
|
|
||||||
|
assertEquals("0.001", Decimal("0.001").toString())
|
||||||
|
assertEquals("0.1", Decimal("0.1").toString())
|
||||||
|
assertEquals("1.0", Decimal("1.0").toString())
|
||||||
|
assertEquals("4.0", Decimal("4.0").toString())
|
||||||
|
assertEquals("4.0", Decimal("4.000").toString())
|
||||||
|
assertEquals("4.0001", Decimal("4.0001").toString())
|
||||||
|
|
||||||
|
assertEquals("-0.001", Decimal("-0.001").toString())
|
||||||
|
assertEquals("-0.1", Decimal("-0.1").toString())
|
||||||
|
assertEquals("-1.0", Decimal("-1.0").toString())
|
||||||
|
assertEquals("-4.0", Decimal("-4.0").toString())
|
||||||
|
assertEquals("-4.0", Decimal("-4.000").toString())
|
||||||
|
assertEquals("-4.0001", Decimal("-4.0001").toString())
|
||||||
|
|
||||||
|
assertEquals("0.0", Decimal("0.000").toString(1))
|
||||||
|
assertEquals("0.00", Decimal("0.000").toString(2))
|
||||||
|
assertEquals("0.000", Decimal("0.000").toString(3))
|
||||||
|
assertEquals("0.001", Decimal("0.001").toString(3))
|
||||||
|
assertEquals("0.00", Decimal("0.001").toString(2))
|
||||||
|
assertEquals("0.0", Decimal("0.001").toString(1))
|
||||||
|
assertEquals("1.0", Decimal("1.001").toString(1))
|
||||||
|
assertEquals("1.1", Decimal("1.101").toString(1))
|
||||||
|
assertEquals("1.10", Decimal("1.101").toString(2))
|
||||||
|
assertEquals("1.101", Decimal("1.101").toString(3))
|
||||||
|
assertEquals("1.1010", Decimal("1.101").toString(4))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Decimal store/load")
|
||||||
fun storeLoad() {
|
fun storeLoad() {
|
||||||
run {
|
run {
|
||||||
val f = Decimal(4, 0.28)
|
val f = Decimal(4.28)
|
||||||
val loaded = Decimal.fromByteArray(f.toByteArray())
|
val loaded = Decimal.fromByteArray(f.toByteArray())
|
||||||
check(f == loaded) { "$f != $loaded" }
|
assertEquals(f, loaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
run {
|
run {
|
||||||
val f = Decimal(32748293658335L, 0.3472302174)
|
val f = Decimal(1)
|
||||||
val loaded = Decimal.fromByteArray(f.toByteArray())
|
val loaded = Decimal.fromByteArray(f.toByteArray())
|
||||||
check(f == loaded) { "$f != $loaded" }
|
assertEquals(f, loaded)
|
||||||
|
}
|
||||||
|
|
||||||
|
run {
|
||||||
|
val f = Decimal(0)
|
||||||
|
val loaded = Decimal.fromByteArray(f.toByteArray())
|
||||||
|
assertEquals(f, loaded)
|
||||||
|
}
|
||||||
|
|
||||||
|
run {
|
||||||
|
val f = Decimal("23784399123654793927324950293475093257324097021387409873240957021934750934089734095273098475")
|
||||||
|
val loaded = Decimal.fromByteArray(f.toByteArray())
|
||||||
|
assertEquals(f, loaded)
|
||||||
|
}
|
||||||
|
|
||||||
|
run {
|
||||||
|
val f = Decimal("23784399123654793927324950293475093257324097021387409873240957021934750934089734095273098475.25")
|
||||||
|
val loaded = Decimal.fromByteArray(f.toByteArray())
|
||||||
|
assertEquals(f, loaded)
|
||||||
|
}
|
||||||
|
|
||||||
|
run {
|
||||||
|
val f = Decimal(32748293658335.3472302174)
|
||||||
|
val loaded = Decimal.fromByteArray(f.toByteArray())
|
||||||
|
assertEquals(f, loaded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +156,7 @@ object DecimalTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ImpreciseFraction serialization")
|
@DisplayName("Decimal serialization")
|
||||||
fun serialization() {
|
fun serialization() {
|
||||||
val output = FastByteArrayOutputStream()
|
val output = FastByteArrayOutputStream()
|
||||||
val outputStream = ObjectOutputStream(output)
|
val outputStream = ObjectOutputStream(output)
|
||||||
|
Loading…
Reference in New Issue
Block a user