A Kotlin multiplatform library for arbitrary precision arithmetics

Overview

pipeline status Maven Central

Kotlin MP BigNum library

Kotlin Multiplatform BigNum library is a pure kotlin implementation of arbitrary precision arithmetic operations. It follows the same approach as Kotlin does on JVM to keep the interface familiar.

Notes & Roadmap

This is an implementation of pure kotlin arbitrary integer and floating-point arithmetic support.

The APIs might change until v1.0

Version 0.3.0 brings API changes to BigDecimal API see changelog for full list.

Also, there is a plan to implement platform native versions.

Testing to verify that the library works properly is mostly done against Java BigInteger and BigDecimal implementations.

Should I use this in production?

The library is still under development, but at the moment it is feature complete, further improvements will be optimizations and bug-fixing.

Integration

Gradle

implementation("com.ionspin.kotlin:bignum:0.3.3")

Snapshot builds

repositories {
    maven {
        url = uri("https://oss.sonatype.org/content/repositories/snapshots")
    }
}
implementation("com.ionspin.kotlin:bignum:0.3.4-SNAPSHOT")

Serialization

Serializers for KotlinX Serializtion library are provided, see more here kotlinx serialization support

Note that because kotlinx doesn't support linux ARM targets as well as MinGW x86, serialization support library doesn't either. Additionally, because of a bug when building serialization support library only JS IR variant is provided.

Usage

Integers

Creating Big Integers

To create a big integer you can parse a string:

BigInteger.parse("-1122334455667788990011223344556677889900", 10)

Or use the extensions or companion function for Long, Int, Byte or Short

val bigIntegerExtension = 234L.toBigInteger()
val bigIntegerCompanion = BigInteger.fromLong(234L)

Or use extensions functions for String

"12345678".toBigInteger()

Basic Arithmetic Operations

Addition

val a = BigInteger.fromLong(Long.MAX_VALUE)
val b = BigInteger.fromInt(Integer.MAX_VALUE)

val sum = a + b
println("Sum: $sum")
----- Output -----
Sum: Sum: 9223372039002259454

Subtraction

val a = BigInteger.fromLong(Long.MIN_VALUE)
val b = BigInteger.fromLong(Long.MAX_VALUE)

val difference = a - b
println("Difference: $difference")
----- Output -----
Difference: -18446744073709551615

Multiplication

val a = BigInteger.fromLong(Long.MAX_VALUE)
val b = BigInteger.fromLong(Long.MIN_VALUE)

val product = a * b

println("Product: $product")
----- Output -----
Product: -85070591730234615856620279821087277056

Division - Quotient

val a = BigInteger.fromLong(Long.MAX_VALUE)
val b = BigInteger.fromInt(Int.MAX_VALUE)

val dividend = a + b
val divisor = BigInteger.fromLong(Long.MAX_VALUE)

val quotient = dividend / divisor
        println("Quotient: $quotient")
----- Output -----
Quotient: 1

Division - Remainder

val a = BigInteger.fromLong(Long.MAX_VALUE)
val b = BigInteger.fromInt(Int.MAX_VALUE)

val dividend = a + b
val divisor = BigInteger.fromLong(Long.MAX_VALUE)

val remainder = dividend % divisor
println("Remainder: $remainder")
----- Output -----
Remainder: 2147483647

Division - Quotient and Remainder

val a = BigInteger.fromLong(Long.MAX_VALUE)
val b = BigInteger.fromInt(Int.MAX_VALUE)

val dividend = a + b
val divisor = BigInteger.fromLong(Long.MAX_VALUE)

val quotientAndRemainder = dividend divrem divisor

println("Quotient: ${quotientAndRemainder.quotient} \nRemainder: ${quotientAndRemainder.remainder}")
----- Output -----
Quotient: 1 
Remainder: 2147483647

Bitwise Operations

Shift Left

val a = BigInteger.fromByte(1)

val shifted = a shl 215
println("Shifted: $shifted")
----- Output -----
Shifted: 52656145834278593348959013841835216159447547700274555627155488768

Shift Right

val a = BigInteger.parseString("100000000000000000000000000000000", 10)

val shifted = a shr 90
----- Output -----
Shifted: 80779

Xor

val operand = BigInteger.parseString("11110000", 2)
val mask = BigInteger.parseString("00111100", 2)

val xorResult = operand xor mask

println("Xor result: ${xorResult.toString(2)}")
----- Output -----
Xor result: 11001100

And

val operand = BigInteger.parseString("FFFFFFFFFF000000000000", 16)
val mask =    BigInteger.parseString("00000000FFFF0000000000", 16)
val andResult = operand and mask
println("And result: ${andResult.toString(16)}")
----- Output -----
And result: ff000000000000

Or

val operand = BigInteger.parseString("FFFFFFFFFF000000000000", 16)
val mask =    BigInteger.parseString("00000000FFFF0000000000", 16)
val orResult = operand or mask
println("Or result: ${orResult.toString(16)}")
----- Output -----
Or result: ffffffffffff0000000000

Binary Not

Unlike Java BigInteger which does two's complement inversion, this method does bitwise inversion,

i.e.:

If the number was "1100" binary, not() returns "0011" => "11" => 4 in base 10
In the same case Java BigInteger would return "1011" => -13 two's complement base 10
val operand = BigInteger.parseString("11110000", 2)
val result = operand.not()
println("Not operation result: ${result.toString(2)}")
----- Output -----
Inv result: 1111

Modular integers

A modInverse function that is equivalent to java BigInteger modInverse is available. Note that this method will produce a BigInteger not a ModularBigInteger

Big integers can be converted to modularIntegers with same modulo, and then inverse() method is available. This method will return ModularBigInteger

val a = 100_002.toBigInteger()
val modularA = a.toModularBigInteger(500.toBigInteger())
println("ModularBigInteger: ${modularA.toStringWithModulo()}")
----- Output -----
ModularBigInteger: 2 mod 500

If you want to create more ModularBigIntegers with the same module, you can retrieve creator by calling getCreator

More inforamtion about the ModularBigIntegers can be found in the third section

Floating Point

Creating

Parsing

To create a BigDecimal you can parse a string in expanded or scientific notation

Scientific

val bigDecimal = BigDecimal.parseString("1.23E-6)")
println("BigDecimal: $bigDecimal")
----- Output -----
BigDecimal: 1.23E-6

Expanded

val bigDecimal = BigDecimal.parseString("0.00000123")
println("BigDecimal: $bigDecimal")
----- Output -----
BigDecimal: 1.23E-6

From Long, Int, Short, Byte

You can convert standard types to BigDecimal, i.e. Long

val bigDecimal = BigDecimal.fromLong(7111)
println("BigDecimal: $bigDecimal")
----- Output -----
BigDecimal: 7.111E+3

Or you can specify an exponent. when you do specify an exponent, input value (long, int, short, byte) is considered to be in scientific notation.

val bigDecimal = BigDecimal.fromLongWithExponent(1, -5L)
println("BigDecimal: $bigDecimal")
println("BigDecimalExpanded: ${bigDecimal.toStringExpanded()}")
----- Output -----
BigDecimal: 1.0E-5
BigDecimalExpanded: 0.00001

Extension functions

For String

val bigDecimal = "12345678.123".toBigInteger

Or for Double of Float

val bigDecimalFromFloat = 123.456f.toBigDecimal() 
val bigDecimalFromDouble = 123.456.toBigDecimal()

Long, Int, Short, Byte

val bigDecimalFromLong = 10.toLong().toBigDecimal() 
val bigDecimalFromInt = 10.toInt().toBigDecimal()
val bigDecimalFromShort = 10.toShort().toBigDecimal() 
val bigDecimalFromByte = 10.toByte().toBigDecimal()

toString

By default toString() is returned in scientific output, but expanded output is also available

val bigDecimal = BigDecimal.parseString("123.456")
println("BigDecimal: ${bigDecimal.toStringExpanded()}")
bigDecimal.toStringExpanded() == "123.456"
----- Output -----
BigDecimal: 123.456

toByteArray and fromByteArray

Converts the BigInteger to and from big endian byte array.

val bigIntOriginal = BigInteger.fromULong(ULong.MAX_VALUE)
val byteArray = bigIntOriginal.toByteArray()
val reconstructed = BigInteger.fromByteArray(byteArray)
println("${bigIntOriginal == reconstructed}")
----- Output -----
true

There are two helper methods when converting from two's complement array (the same form that Java BigInteger provides):

  • fromTwosComplementByteArray
val negativeInput = ubyteArrayOf(0xFFU, 0x55U, 0x44U, 0x34U)
val negativeBigInt = BigInteger.fromTwosComplementByteArray(negativeInput.asByteArray())
  • toTwosComplementByteArray
val negativeBigInt = BigInteger.parseString("-AABBCC", 16)
val negativeBigIntArray = negativeBigInt.toTwosComplementByteArray()

Arithmetic operations

Standard arithmetic operations that are present:

  • Addition
  • Subtraction
  • Multiplication
  • Division
  • Exponentiation
  • Increase by one
  • Decrease by one
  • Absolute value
  • Negate
  • Signum

(Suspiciously missing is square root, should be added soon™)

Operations are executed with existing significands and then rounded down afterwards. Decimal mode parameter controls the precision and rounding mode

DecimalMode

This is a counterpart to the Java BigDecimal MathContext and scale at the same time. Decimal mode API is under revision and will be improved during 0.3.0-0.4.0 library lifecycle

data class DecimalMode(val decimalPrecision : Long = 0, val roundingMode : RoundingMode = RoundingMode.NONE, val scale: Long = -1)

decimalPrecision defines how many digits should significand have

roundingMode defines rounding mode.

Decimal mode resolution
  • DecimalMode supplied to the operation always overrides all other DecimalModes set in BigDecimals

  • If a DecimalMode is set when creating a BigDecimal that mode will be used for all operations.

  • If two BigDecimals have different DecimalModes with different RoundingModes an ArithmeticException will be thrown. If the modes are same, but the precision is different, larger precision will be used.

Scale

Scale, or the number of digits to the right of the decimal, can also be specified. Default is no scale, which puts no restriction on number of digits to the right of the decimal. When scale is specified, a RoundingMode other than RoundingMode.NONE is also required. When arithmetic operations have both operands unlimited precision and no scaling, the result is also unlimited precision and no scale. When an operation mixes an unlimited precision operand and a scaled operand, the result is unlimited precision. WHen both operands have scale, whether unlimited precision or limited precision, then these rules for scale of the result are used:

  • add, subtract - max of the two scales
  • multiply - sum of the two scales
  • divide - min of the two scales
Infinite precision

Precision 0 and roundingMode none attempt to provide infinite precisions. Exception is division (and exponentiation with negative parameter), where default precision is the sum of precisions of operands (or 6, if the sum is below 6). If result of the operation cannot fit inside precision and RoundingMode is NONE, ArithmeticException will be thrown.

Example from the tests:

   fun readmeDivisionTest() {
        assertFailsWith(ArithmeticException::class) {
            val a = 1.toBigDecimal()
            val b = 3.toBigDecimal()
            val result = a/b
        }

        assertTrue {
            val a = 1.toBigDecimal()
            val b = 3.toBigDecimal()
            val result = a.div(b, DecimalMode(20, RoundingMode.ROUND_HALF_AWAY_FROM_ZERO))
            result.toString() == "3.3333333333333333333E-1"
        }
    }

Convenience rounding methods

BigDecimal class contains two convenience rounding methods, the roundToDigitPositionAfterDecimalPoint(digitPosition: Long, roundingMode: RoundingMode) which rounds to a specific position after the decimal point, like in the following example:

        assertTrue {
            val rounded = BigDecimal.fromIntWithExponent(123456789, 3)
                .roundToDigitPositionAfterDecimalPoint(3, RoundingMode.CEILING)
            rounded.toStringExpanded() == "1234.568"
        }

and roundToDigitPosition(digitPosition: Long, roundingMode: RoundingMode) which rounds to a specifi digit precision regardless of decimal point, like in the following example:

        assertTrue {
            val rounded = BigDecimal.parseString("1234.5678")
                .roundToDigitPosition(3, RoundingMode.ROUND_HALF_TOWARDS_ZERO)
            rounded.toStringExpanded() == "1230"
        }

        assertTrue {
            val rounded = BigDecimal.parseString("0.0012345678")
                .roundToDigitPosition(4, RoundingMode.ROUND_HALF_TOWARDS_ZERO)
            rounded.toStringExpanded() == "0.001"
        }

Rounding modes

Name Description
FLOOR Towards negative infinity
CEILING Towards positive infinity
AWAY_FROM_ZERO Away from zero
TOWARDS_ZERO Towards zero
NONE Infinite decimalPrecision, and beyond
ROUND_HALF_AWAY_FROM_ZERO Round towards nearest integer, using towards zero as tie breaker when significant digit being rounded is 5
ROUND_HALF_TOWARDS_ZERO Round towards nearest integer, using away from zero as tie breaker when significant digit being rounded is 5
ROUND_HALF_CEILING Round towards nearest integer, using towards infinity as tie breaker when significant digit being rounded is 5
ROUND_HALF_FLOOR Round towards nearest integer, using towards negative infinity as tie breaker when significant digit being rounded is 5

Modular Integers

Modular arithmetic operations are supported only between integers with the same modulo.

Creating Modular Integers

First define the modulo you are going to use by getting an instance of the creator, and than use that creator to create instances of modular integers

val creator = ModularBigInteger.creatorForModulo(100)
val modularBigInteger = creator.fromLong(150)
println("ModularBigInteger: ${modularBigInteger.toStringWithModulo()}")
----- Output -----
ModularBigInteger: 50 mod 100

Otherwise behavior is similar to normal integers

Sources

For examples of rounding modes consult Comparison of approaches for rounding to an integer on Wikipedia

This library draws inspiration from libraries like Java BigInteger, GNU MP Arithmetic Library, Javolution JScience, as well as following literature

Modern Computer Arithmetic
Richard P. Brent and Paul Zimmermann
Version 0.5.9 of 7 October 2010
Hacker`s Delight
Henry S. Warren, Jr.
Second Edition
Art of Computer Programming, Volume 2: Seminumerical Algorithms
Donald E. Knuth
3rd Edition
Refinement of a newton reciprocal algorithm for arbitrary precision numbers
Yiping Cheng, Ze Liu

And many other blogs and posts scattered over the internet.

If you want to try building BigNum library yourself, those are the sources I would recommend to start with.

Development environment

If you are planning on contributing to the development of the library, you can set a local gradle variable in gradle.properties in your gradle home directory (i.e. on Linux ~/.gradle/gradle.properties) called bignumPrimaryDevelopmentOs to linux, windows or mac so that the gradle builds JVM and JS targets on your platform. The reason for this switch is that most of the test are run on JVM by comparing results to Java BigInteger/Decimal so they should be run on your main development OS to verify proper results, and can be skipped on other operating systems where you are developing that platform specific features.

And thank you for contributing!

Comments
  • BigDecimal narrowing

    BigDecimal narrowing

    It would be nice if BigDecimal implemented the (currently internal) interface in BigNumber of NarrowingOperations to support changing BigDecimals back to all the various basic types; Int, Long, Short, Float, Double, UInt, UShort, ULong, Byte and UByte,

    Since BigDecimal is not "open" extension functions are required to do this. An example of one is below.

    /**
     * Returns a long value that truncates any digits to the right of the decimal.
     *
     * @param exactRequired if true and non-zero truncation error would result
     * @throws ArithmeticException if the value is to large or too small for a long, or if
     * exactRequired true and value to right of decimal is non-zero
     */
    fun BigDecimal.longValue(exactRequired: Boolean = false): Long {
        val limit = BigDecimal.fromLong(Long.MAX_VALUE)
        val lowLimit = BigDecimal.fromLong(Long.MIN_VALUE)
        if (this > limit || this < lowLimit)
            throw ArithmeticException("Narrowing to Long would lose significant digits")
        return checkTruncation(exactRequired)
    }
    
    private fun BigDecimal.checkTruncation(exactRequired: Boolean):Long {
        val result = this.divideAndRemainder(BigDecimal.ONE)
        if (exactRequired && !result.second.isZero()) {
            throw ArithmeticException("Narrowing would lose precision")
        }
        val work = this.minus(result.first)
        return work.significand.longValue(exactRequired)
    }
    

    This would provide a superset of the narrowing operations provided in java's BigDecimal

    enhancement 
    opened by skolson 20
  • Incorrect results of calculations

    Incorrect results of calculations

    Describe the bug Hello. I'm migrating to Kotlin/Native and use your BigNum library as actual declaration for BigDecimal. on JVM I have Java's BigDecimal. Many of my tests are failing because the result of calculation on Native with your lib is different. I provide some examples. Since the migration to native is a long process I can't know all possible errors with the calculations because I comment failing tests one-by-one. The more I comment, the more errors I see. If I may I'll add more errors later.

    To Reproduce All this values are not equal to what your lib show. So you can just compare both libs and find a cause of different result.

    
    println(BigDecimal("1").divide(BigDecimal("110"), 15,RoundingMode.CEILING ).toPlainString())
    
    println(BigDecimal("6075.6").divide(BigDecimal("0.5"), 0,RoundingMode.CEILING ).toPlainString())
    
    println(BigDecimal("6075.7").divide(BigDecimal("0.5"), 0,RoundingMode.CEILING ).toPlainString())
    
    println(BigDecimal("6075.8").divide(BigDecimal("0.5"), 0,RoundingMode.CEILING ).toPlainString())
    
    println(BigDecimal("6075.9").divide(BigDecimal("0.5"), 0,RoundingMode.CEILING ).toPlainString())
    
    println(BigDecimal("0.001294914096").divide(BigDecimal("0.000001"), 0,RoundingMode.CEILING ).toPlainString())
    
    println(BigDecimal("93545.3695").divide(BigDecimal("1"), 0,RoundingMode.FLOOR ).toPlainString())
    
    

    Another problem I met is that I can't leave decimalPrecision like default value 0 when have a scale specified. Which is strange because default value 0 should be "unlimited precision" as per the docs. I always have to provide it. I think it's not right. I only interested in scale and don't need to limit precision. Right?

    Expected behavior I think the results of the lib should be the same as in Java's BigDecimal

    Also there is a difference of how BigNum shows output of toExpandedString in comparison to Java's solution. It always strips zeroes from the end of a string. Is it expected? If so, why? In some places it's useful to have a text like "0.00" which gives a hint on scale of the number shown in UI. Maybe it would be a good idea to show zeroes at the end and make another function like toExpandedWithoutZeroes() or even better to change toPlainString() to show zeroes? That for would be great. Both functions will be useful then. What to you think?

    Platform Linuxx64, JVM

    Additional context Add any other context about the problem here.

    bug 
    opened by avently 15
  • Rounding Issue

    Rounding Issue

    Hi @ionspin !

    I am having problem with figure rounding. I have an application that can support trailing decimal point number configuration. We have a setting that will configure this thing, so it will be dynamic.

    Let's say the total decimal point number set is 3. So given number 12.5671, it will round to 12.567, if it is 2, it becomes 12.56 and so on.

    I have a test case like following

    image

    and here is my round function

    image

    The purpose why I convert to BigInteger first is to get the number before the decimal point, and then use the value from the setting to add the trailing decimal point. The test case above will result to java.lang.reflect.InvocationTargetException

    Can you guide me on how to directly use the BigDecimal number and then use my custom setting value to set the trailing decimal point number?

    If it is still unclear to you, please let me know, so I can explain more to you. Thanks for this amazing library!

    enhancement 
    opened by avjiang 14
  • First draft of fixes #111, fixes #118

    First draft of fixes #111, fixes #118

    Narrowing functions that resolves #111 are mostly working. Fix #118 involved not using a roundDiscarded function when using unlimited precision. Also did some minor cleanup of Lint warnings in BigDecimal. Still needs comments, and some changes to BigInteger to allow unit tests to work in certain cases, and probably other stuff.

    opened by skolson 13
  • Performing two sequential BigDecimal exponentiations hangs

    Performing two sequential BigDecimal exponentiations hangs

    Describe the bug When performing two sequential BigDecimal exponentiations, the second exponentiation will hang.

    To Reproduce Run the following code:

    val decimalMode = DecimalMode(6L, RoundingMode.CEILING, -1L)
    
    val a = BigDecimal.parseStringWithMode("-8.40575E+306", decimalMode)
    val b = BigDecimal.parseStringWithMode("-1.77408E+308", decimalMode)
    
    a.pow(b.longValue(false))
    
    val c = BigDecimal.parseStringWithMode("-3.74044E+307", decimalMode)
    val d = BigDecimal.parseStringWithMode("-2.22553E+8", decimalMode)
    
    c.pow(d.longValue(false)) // Hangs here
    

    Expected behavior Both exponentiations complete without issue.

    Platform JVM and JS

    Additional context For me this occurs on a multiplatform project with the browser JS and JVM targets.

    not an issue 
    opened by JoonasC 12
  • Conversion to double off by power of 10

    Conversion to double off by power of 10

    Given this JVM snippet:

        val testDouble = 999111.999111
        val testBig = BigDecimal.fromDouble(testDouble)
        val newDouble = testBig.doubleValue(true)
        assertEquals(testDouble, newDouble, 0.0)
    

    assert should work, but assert fails: java.lang.AssertionError: Expected :999111.999111 Actual :9991119.99111

    I haven't tried other scenarios yet, have not finished debugging. This was using 0.2.0.

    The problematic line in BigDecimal.kt is:

                this.significand.longValue(true).toDouble() / double10pow[exponent.toInt()]
    

    In debugger, state of BigDecimal before this line is this:

    this = {BigDecimal@859} "9.99111999111E+5" precision = 12 precisionLimit = 0 roundingMode = {RoundingMode@864} "NONE" significand = {BigInteger@865} "999111999111" exponent = 5 scale = -1 usingScale = false decimalMode = null

    It looks like this should work but it doesn't. Still digging.

    Environment is AS 4.1, Kotlin 1.4.10

    opened by skolson 11
  • Multiply looses precission

    Multiply looses precission

    Describe the bug Numbers rounded to specific number of of digits after decimal point multiply incorrectly.

    To Reproduce Steps to reproduce the behavior:

    val a = "5.61".toBigDecimal().roundToDigitPositionAfterDecimalPoint( 2, RoundingMode.ROUND_HALF_AWAY_FROM_ZERO)
    val b = "2.95".toBigDecimal().roundToDigitPositionAfterDecimalPoint( 2, RoundingMode.ROUND_HALF_AWAY_FROM_ZERO)
    val res = a.multiply(b).roundToDigitPositionAfterDecimalPoint( 2, RoundingMode.ROUND_HALF_AWAY_FROM_ZERO)
    assertEquals("16.55", res.toStringExpanded())
    
    expected:<16.5[5]> but was:<16.5[]>
    Expected :16.55
    Actual   :16.5
    

    Expected behavior

    Result should not loose precision same way java BigDecimal.setScale(2, RoundMode.HALF_UP)

    BigDecimal bd1 = new BigDecimal("5.61").setScale(2, RoundingMode.HALF_UP); 
    BigDecimal bd2  = new BigDecimal("2.95").setScale(2, RoundingMode.HALF_UP); 
    BigDecimal bs3 = bd2.multiply(bd1).setScale(2, RoundingMode.HALF_UP); 
    String str = "Result is " +bs3;
    System.out.println(str); 
    

    Platform JVM (commonTest run by testDebugUnitTest)

    opened by lhoracek 10
  • BigDecimal divideAndRemainder bad result

    BigDecimal divideAndRemainder bad result

    Given this code:

    val x = BigDecimal.fromShort(Short.MIN_VALUE)
    val result = x.divideAndRemainder(BigDecimal.ONE)
    

    x is displayed in debugger with correct value -3.2768E+4. Result.first should be the same, and result.second should be zero. Instead, this is displayed in debugger: result.first = -3.2769E+4 result.second = 1.0

    So far this is the only case I've hit in early unit testing, so am unsure what other ways the underlying bug may manifest. It's possible this happens whenever signs of numerator and divisor differ.

    Environment: kotlin 1.4.0-rc com.ionspin.kotlin:bignum:0.1.6-1.4.0-rc-SNAPSHOT running junit5 unit test using Kotest 4.2.0 invoking multiplatform common functions using BigDecimal

    bug 
    opened by skolson 10
  • Exception when converting negative numbers

    Exception when converting negative numbers

    Describe the bug There is an exception thrown when processing negative numbers

    To Reproduce

    println((-6.0).toBigDecimal().toStringExpanded().toDouble()) 
    

    Expected behavior

    The library will process the number same as positive numbers
    

    Version : 0.1.5-SNAPSHOT

    image

    Image above is the reference

    opened by avjiang 10
  • BigDecimal floatValue and fromFloat have same bug as doubles had in issue #130

    BigDecimal floatValue and fromFloat have same bug as doubles had in issue #130

    See the description of issue #130, the same exact bugs are in floatValue and fromFloat. Sorry I should have caught this during the issue 130 work, Anyway while unit testing the fix to the two float functions I hit a different bug/feature and am unsure which it is - bug or feature. Given this code snippet:

            f = BigDecimal.fromFloat(Float.MIN_VALUE)
            assertEquals(Float.MIN_VALUE, f.floatValue(true))
            val f2 = f.divide(BigDecimal.TEN)
    

    The value of Float.MIN_VALUE is 1.4E-45. As expected f2 is 1.4E-46. But here is the state inside f2:

    f2 = {BigDecimal@2111} "1.4E-46"
     precision = 11
     precisionLimit = 0
     roundingMode = {RoundingMode@1966} "NONE"
     significand = {BigInteger@1937} "14000000000"
     exponent = -46
     scale = -1
     usingScale = false
     decimalMode = {DecimalMode@1903} "DecimalMode(decimalPrecision=0, roundingMode=NONE, scale=-1)"
    

    is the precision number and the significand correct in this case? Or should all those trailing zeroes have been trimmed and the precision set to 2? If this is correct behavior lemme know, because there is current code in the narrowing stuff that expected this result to have precision = 2 and significand = 14. Because it doesn't, the precision limit check in floatValue() is incorrect. And it likely there are cases where the doubleValue() precision limit check is incorrect for the same reason. Both use the precision value in the same way as a limit check.

    Let me know what the correct behavior of precision and significand is in the above case, and I'll proceed from there to get fixes and unit tests developed for the above.

    opened by skolson 8
  • BigDecimalWhole.isWholeNumber() wrong when DecimalMode set

    BigDecimalWhole.isWholeNumber() wrong when DecimalMode set

    Describe the bug BigDecimalWhole.isWholeNumber() wrong when DecimalMode set

    To Reproduce

        @Test
        fun testIsWhole() {
            val bigDecimalWhole = "1.1234567826".toBigDecimal(
                decimalMode = DecimalMode(18, RoundingMode.ROUND_HALF_CEILING, 18)
            ).moveDecimalPoint(9)
    
            assertEquals("1123456782.6", bigDecimalWhole.toPlainString())
            assertEquals(false, bigDecimalWhole.isWholeNumber())
        }
    

    Platform JVM and JS

    Additional context Works correct when no DecimalMode is set, but I have to set it to work around #237

    opened by MrStahlfelge 7
  • Conversion from java.math

    Conversion from java.math

    Is your feature request related to a problem? Please describe. I am unable to obtain a com.ionspin.kotlin.bignum.integer.BigInteger from a java.math.BigInteger.

    Describe the solution you'd like An extension method on java.math.BigInteger to perform the conversion.

    Describe alternatives you've considered Implementing it myself on each project.

    enhancement good first issue 
    opened by Gaming32 0
  • Provides kotlin collection operators like sumOf (convenience)

    Provides kotlin collection operators like sumOf (convenience)

    Is your feature request related to a problem? Please describe. Kotlin collections provides helper method like sumOf { }, but some methods are defined with the explicit types (there is a Iterable<T>.sumOf(selector: (T) -> Long): Long and a Iterable<T>.sumOf(selector: (T) -> Int): Int for example).

    data class Foo(val data: Double)
    val fooList = listOf(Foo(0.1), Foo(0.2))
    val sum = fooList.sumOf { it.data }
    

    It could be nice to provide those methods for BigInteger and BigDecimal.

    Describe the solution you'd like

    data class Bar(val data: BigDecimal)
    val barList = listOf(Bar(BigDecimal.fromDouble(0.1)), Bar(BigDecimal.fromDouble(0.2)))
    val sum = barList.sumOf { it.data }
    

    Describe alternatives you've considered

    It's still possible to use fold to compute a sum (just a bit less convenient).

    val sum2 = barList.fold(BigDecimal.ZERO) { acc, it -> acc + it.data }
    
    enhancement 
    opened by glureau 0
  • Support for radices other than 10

    Support for radices other than 10

    Is your feature request related to a problem? Please describe.

    The calculator app I've written (not currently using this library, but I'm certainly considering it) currently works in bases 10 and 12, and I'm considering adding support for base 16.

    BigDecimal is (as the name says) only for base 10, not allowing me to pass in a radix.

    Describe the solution you'd like

    A BigNumber class or similar where the radix can be specified would be ideal. BigDecimal could possibly remain as a specialisation of a big number for base 10, but any radix-independent methods could be moved to the superclass.

    Describe alternatives you've considered

    a. do all the maths in decimal and convert each way. I'd get rounding errors here and there, but it can't be any worse than using double precision. b. make my own BigRational or BigNumber utilities, possibly building on top of BigInteger.

    enhancement 
    opened by hakanai 0
  • Added some series-based functions (exp, cos, sin, ...)

    Added some series-based functions (exp, cos, sin, ...)

    Added:

    • exp (by infinite series)
    • cos (by infinite series)
    • sin (by infinite series)
    • tan (by cos/sin)
    • cosh (by infinite series)
    • sinh (by infinite series)
    • tanh (by cosh/sinh)

    Not added:

    • ln - might not be too hard but converges very slowly so need another trick
    • arcsin(x) = arctan(x / √(1 - x²))
    • arccos(x) = arctan(√(1 - x²) / x)
    • arctan - conceivably possible to do similarly, but the series only converges for small values, so need to use Euler's trick
    • arsinh(x) = ln(√(1 + x²) + x)
    • arcosh(x) = ln(x + √(x - 1)√(x + 1)) = ln(√(x² - 1) + x)
    • artanh(x) = ½ ln(x + 1) - ½ ln(1 - x)
    opened by hakanai 4
  • [Question] Lacking advanced math features

    [Question] Lacking advanced math features

    Currently this library is lacking advanced math features, such as: sin, cos, sqrt, nroot, log, etc.

    Are these within the scope of this library?

    If not, what workaround would you suggest?

    enhancement help wanted 
    opened by JoonasC 4
Releases(0.3.7)
  • 0.3.7(Aug 6, 2022)

    • Bump to Kotlin 1.7.10
    • Fix for #239, toPlainString which was supposed to return same result as JVM was truncating zeroes when scale was used.
    • Fix for #238, wrong exponent in resolved decimal precision was used in divideAndRemainder
    • Fix for #237, when precision and exponent are same invalid value was returned
    • Fix for #231, exception incorrectly thrown when using scale (the library was only checking for unlimited precision instead of that and presence of scale)
    Source code(tar.gz)
    Source code(zip)
  • 0.3.6(May 22, 2022)

    • Provide big integer/big decimal to their java counterpart conversion methods. They are slow as they rely on string conversion. #230
    • Update to Kotlin 1.6.21
    • Fix for #229, incorrect doubleValue result when exact result is not required and significand is larger than Long.MAX_VALUE
    Source code(tar.gz)
    Source code(zip)
  • 0.3.4(Jan 15, 2022)

    • Throw a specific exception when exponentiation of zero with negative exponent is attempted (#206)
    • Remove zero counting debug log (#210)
    • Fix for invalid decimal precision when dividend has exponent -1 (#195)
    • API CHANGE Narrowing function (longValue, intValue, doubleValue...) are now defaulting to exactRequired which means they will throw ArithmeticException if the conversion cannot be done without loss.
    • Use temporary javascript comparison workaround to handle precision loss. This is fixed in kotlin 1.6.20 and the workaround will be removed once that is released.
    Source code(tar.gz)
    Source code(zip)
  • 0.3.3(Nov 9, 2021)

  • 0.3.2(Sep 5, 2021)

    • Added kotlinx serialization support library
    • Enabled gradle dependencies verification (bootstrapped)
    • Fix for losing decimal mode when using unary minus (#184)
    • Fix for losing sign when narrowing to long from big integer (#186)
    Source code(tar.gz)
    Source code(zip)
  • 0.3.1(May 10, 2021)

    • Fix for #176, a case of unclear API. Methods roundToDigitPositionAfterDecimalPoint and roundToDigitPosition would set decimal precision to the number of digits present in the result after the rounding was completed. Now they only set decimal precision if it's explicitly set, otherwise it stays unlimited.
    • Bump to Kotlin 1.5.0
    • Including Kotlin 1.4.32 release to support projects using that Kotlin version and unsigned integers.
    Source code(tar.gz)
    Source code(zip)
  • 0.3.0(Apr 17, 2021)

    • Fixed losing scale when there is a carry in addition in BigDecimal.
    • Fixed BigInteger numberOfDecimalDigits which would return 0 for 0 instead of 1.
    • Fixed #168 - Fix invalid rounding results Development roadmap for version 0.3.0 states 'API statbilization' so:
    • API CHANGE Extensions functions used to create BigDecimal from primitive types have been split into 'toBigDecimal(exponentModifier...)' and 'toBigDecimalUsingSignificandAndExponent(exponent...)' to bring more clarity to the API and solve #164
    • API CHANGE BigInteger uses a sum of magnitude elements as part of hash code instead of array hash code as it wasn't stable on JS
    • API CHANGE BigDecimal hashCode doesn't include decimal mode anymore, so it respects contract that hash code equals another hash code when equals returns true.
    • API CHANGE BigDecimal equals doesn't consider decimal mode anymore. Two big decimals are now equal only if they represent the same number.
    • Fixed decimal mode precision/scale mismatch in BigDecimal #161
      • NOTE: API CHANGE final precision is now desiredPrecision + scale
    • Add @SharedImmutable to BigInteger to support native multithreading #159
    Source code(tar.gz)
    Source code(zip)
  • 0.2.8(Feb 15, 2021)

    • Fixed support for watchosX64/watchos86 (#150)
    • Fixed parsing characters outside of radix range (#152)
    • Fixed invalid byte array intialization (#153)
    • Fixed rounding KDoc (#156)
    Source code(tar.gz)
    Source code(zip)
  • 0.2.7(Feb 6, 2021)

    Same release as 0.2.4, but republished because of issues when uploading to Maven Central. https://status.maven.org/incidents/z70skgbq8vgc

    Source code(tar.gz)
    Source code(zip)
  • 0.2.4(Feb 5, 2021)

    • Bump to kotlin 1.4.30
    • Fix invalid to string when big decimal is zero (#148)
    • Fix xor test which was previously testing bit shifting instead of xor (#147)
    • Fix biginteger xor operands magnitude array length mismatch (#144)
    • Fix biginteger bitwise operations sign (#142)
    • Added (back) support for legacy js target (#138)
    Source code(tar.gz)
    Source code(zip)
  • 0.2.3(Nov 28, 2020)

    • Bump to Kotlin 1.4.20
    • Fix for invalid exponent rounding and string representation (#139)
    • Returned mingwx86 target (#137)
    • Fixed #134
    • Fixed #130 Conversion to double off by power of 10
    • Fixed #132 BigDecimal narrow functions toFloat() and toDouble, with exactRequired = false sometimes wrongly fail
    • Fixed floatValue and doubleValue narrowing functions (pull request #135)
    • Fixed invalid string parsing of big decimals
    Source code(tar.gz)
    Source code(zip)
  • 0.2.2(Oct 10, 2020)

    0.2.2 - 10.10.2020 - Rework infinite precision division Issue #127 - invalid division when using unlimited precision wasn't completely handled in the previous release. With this release that division case was completely reworked and additional tests were added.

    Source code(tar.gz)
    Source code(zip)
  • 0.2.1(Oct 8, 2020)

  • 0.2.0(Aug 18, 2020)

    • Improvement #122 Add "scale" support to BigDecimal
    • Fixed #118 Rounding issue on division
    • Improvement #116 Introduce local gradle varibale to enable JVM target on different platforms
    • Fixed #112 BigDecimal divideAndRemainder bad result
    • Fixed #111 - BigDecimal narrowing
    • Fixed #104 - BigInteger.toByteArray should prefer ByteArray over Array
    • Fixed #103 - Removed coroutines
    • Fixed #90 - Incorrect Result From Negative Double
    • Fixed #88 - BigInteger.bitAt does not work correctly
    • Fixed #86 - Rounding fails when integer part is 9
    • Fixed #88 - BigInteger.bitAt() returns invalid value
    • Built with Kotlin 1.4.0
    • Reworked to(U)ByteArray conversion methods
      • from and to conversions were not consistent, from(U)ByteArray expected a string of bytes ordered in little or big endian, while to(U)ByteArray produced Int or Long represented as byte with little endian or big endian order.
      • Replaced with a consistent to and from byte/ubyte array conversions
        • fromUByteArray always expects a big-endian ordered array of unsigned bytes
        • fromByteArray always expects a big-endian ordered array of bytes
        • toUByteArray produces unsigned byte array in big-endian order
        • toByteArray produces signed byte array in big-endian order
      • There are two helper methods that convert to and from a two's complement ByteArray, this form conforms to Java BigIntegers toByteArray
        • fromTwosComplementByteArray expects a two's complement ByteArray with at least one sign bit
        • toTwosComplementByteArrayproduces a two's complement ByteArray with at least one sign bit
    • Added secureOverwrite to BigNumber interface, with role of overwriting backing structures with zeroes. It's meant to be used by libraries that require such a functionlity (i.e. crypto). The function also breaks immutability contract of BigNumber implementations, and further operations with that instances have undefined results.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.5(Jan 7, 2020)

    • Version bump to kotlin 1.3.61
    • Gradlew wrapper bump to 6.0.1
    • Added Linux Arm 64, and Linux Arm 32 HFP build targets
    • Fixed smaller BigDecimal issues (#71 #72 #78)
    Source code(tar.gz)
    Source code(zip)
  • 0.1.4(Dec 10, 2019)

    • Main library now has dependancies only on the kotlin standard library (for now, coroutines will be coming back at some point in the future).
    • Renamed BigDecimal round method to roundSignificand, as it describes what it does more precisely
    • Added roundAtDigitPosition and roundAfterDecimalPoint convenience methods.
    • Use Long instead of BigInteger for BigDecimal exponent.
    • Adding MingwX64 and MingwX86 targets.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.3(Nov 19, 2019)

    • Updated kotlin to 1.3.60
    • Updated gradle to 5.6.1, so gradle metada is now version 1.0
    • Cleaned up dependencies, coroutines are now only a test dependency
    Source code(tar.gz)
    Source code(zip)
  • 0.1.2(Nov 17, 2019)

    • Implemented handling of additional empty words which improved speed and number of allocations.
    • Implemented square-and-multiply exponentiation algorithm instead of naive approach.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.1(Oct 19, 2019)

    • Implemented Toom-Cook-3 multiplication, although still slow because of inefficient division
    • Bumped gradle version to 5.6.1, which means that the published Gradle Metadata will be 1.0, making metadata resolution available only on Gradle >= 5.3
    • Fixed several issues related to big decimal comparison, modular integer sign, etc.
    • Added more BigDecimal extension functions
    • Added ModularBigInteger extension functions
    • Added Karatsuba multiplication
    • Added copy and moveDecimalPoint methods
    • Added fromUByteArray and toUByteArray
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Jul 31, 2019)

    • Added toByteArray and fromByteArray
    • Added toFloat and toDouble to BigInteger and ModularBigInteger classes
    • Added BigInteger creation from Float and Double by using tryFromFloat and tryFromDouble, with optional exact parameter to preserve precision.
    • Added BigInteger comparison with Float and Double
    • Added BigDecimal configuration option to switch to expanded representation instead of scientific when calling toString()
    • Improved ModularBigInteger exponentiation algorithm, based on Bruce Schneier Applied Cryptography pesudocode
    Source code(tar.gz)
    Source code(zip)
  • 0.0.9(May 11, 2019)

    • Added modular integers - ModularBigInteger
    • Added modInverse method to BigInteger
    • Extracted interfaces that model big numbers better (BigNumber interface and accompanying interfaces)
    • Implemented integer reciprocal based on newton iteration (Based on paper by Yiping Cheng, Ze Lie : Refinement of a newton reciprocal algorithm for arbitrary precision numbers)
    • Implemented division by reciprocal multiplication. Not used by division at the moment as it is unoptimized and slower than basecase division in early benchmarks.
    • Fixed a bug in Int32 shift right when shift amount was an exact multiple of word size
    • Added constructor overloads
    • Added value methods (intValue, longValue...)
    • Renamed invPrecise() bigInteger method to not()
    • Renamed numberOfDigits() to numberOfDecimalDigits()
    • Introduced BigNumber and BitwiseOperations interfaces
    • Added iOS ARM 32bit support

    Also added missing methods to BigInteger, so BigDecimal and BigInteger are mostly on par with their Java counterpart, in regards to functionality.

    Source code(tar.gz)
    Source code(zip)
  • 0.0.8(Apr 2, 2019)

    This release doesn't bring any new functionality except for a full release of both linux (already present in 0.0.7) built and macos (new in 0.0.8) built artifacts (iosX64, iosArm, macosX64)

    From this release forward snapshot releases will be published as well.

    Source code(tar.gz)
    Source code(zip)
  • 0.0.7(Mar 31, 2019)

    Since this is first GitHub release, well list everything that 0.0.7 has so far:

    • BigInteger support
    • BigDecimal support

    Known issues:

    • Library should be relatively slow, because it doesn't yet have optimized algorithms such as Karatsuba, etc.
    • Square root is not implemented (so not on par with Java BigInteger and BigDecimal)
    • Greatest common divisor is not implemented (same as square root)
    Source code(tar.gz)
    Source code(zip)
Owner
Ugljesa Jovanovic
Ugljesa Jovanovic
Dependency Injection library for Kotlin Multiplatform, support iOS and Android

Multiplatform-DI library for Kotlin Multiplatform Lightweight dependency injection framework for Kotlin Multiplatform application Dependency injection

Anna Zharkova 32 Nov 10, 2022
Kotlin multiplatform library template.

template-kmp-library Kotlin multiplatform library template. Has a baseline setup for a multiplatform library supporting all kotlin targets except andr

Martynas Petuška 51 Nov 21, 2022
Generic AST parsing library for kotlin multiplatform

kotlinx.ast kotlinx.ast is a generic AST (Abstract Syntax Tree) parsing library, Kotlin is currently the only supported language. The library is desig

null 235 Dec 29, 2022
Server Sent Events (SSE) client multiplatform library made with Kotlin and backed by coroutines

OkSSE OkSSE is an client for Server Sent events protocol written in Kotlin Multiplatform. The implementation is written according to W3C Recommendatio

BioWink GmbH 39 Nov 4, 2022
A local storage management library for Kotlin Multiplatform Mobile iOS and android

A local storage management library for Kotlin Multiplatform Mobile iOS and android Features iOS and Android local storage in one interface Provides ge

LINE 20 Oct 30, 2022
Kotlin multiplatform library template

template-kmp-library Kotlin multiplatform library template. Has a baseline setup for a multiplatform library supporting all kotlin targets except depr

Jamie Astley 0 Dec 6, 2021
Kotlin Multiplatform (KMP) library for reading resources in tests

kotlinx-resources Kotlin Multiplatform (KMP) plugin and library that add support for reading resources in tests. The plugin and a library work in tand

Gonçalo Silva 29 Dec 27, 2022
Kotlinx-murmurhash - Kotlin Multiplatform (KMP) library for hashing using MurmurHash

kotlinx-murmurhash Kotlin Multiplatform (KMP) library for MurmurHash, a non-cryp

Gonçalo Silva 23 Dec 27, 2022
Semantic Versioning library for Kotlin Multiplatform.

kotlin-semver Semantic Versioning library for Kotlin Multiplatform. It implements the full semantic version 2.0.0 specification and provides ability t

Peter Csajtai 33 Dec 29, 2022
NSErrorKt - A Kotlin Multiplatform Library to improve NSError interop

NSErrorKt A Kotlin Multiplatform Library to improve NSError interop. WARNING: Th

Rick Clephas 30 Nov 23, 2022
A Kotlin multiplatform unit testing library inspired by / similar to Google Truth.

Truthish A testing API inspired by Google Truth but rewritten in Kotlin from the ground up, so it can be used in Kotlin multiplatform projects. For ex

Varabyte 70 Nov 2, 2022
A library for working with URIs in Kotlin Multiplatform

Uri KMP Most of this work is derived from AOSP's Uri: Uri.java UriCodec.java UriTest.java UriCodecTest.java Gradle Groovy repositories { mavenCentra

Eliezer Graber 10 Jan 4, 2023
A tiny Kotlin multiplatform library that assists in saving and restoring objects to and from disk using kotlinx.coroutines, kotlinx.serialisation and okio

Store A tiny Kotlin multiplatform library that assists in saving and restoring objects to and from disk using kotlinx.coroutines, kotlinx.serialisatio

Isuru Rajapakse 98 Jan 3, 2023
Opinionated Redux-like implementation backed by Kotlin Coroutines and Kotlin Multiplatform Mobile

CoRed CoRed is Redux-like implementation that maintains the benefits of Redux's core idea without the boilerplate. No more action types, action creato

Kittinun Vantasin 28 Dec 10, 2022
An app architecture for Kotlin/Native on Android/iOS. Use Kotlin Multiplatform Mobile.

An app architecture for Kotlin/Native on Android/iOS. Use Kotlin Multiplatform Mobile. 项目架构主要分为原生系统层、Android/iOS业务SDK层、KMM SDK层、KMM业务逻辑SDK层、iOS sdkfra

libill 4 Nov 20, 2022
Dependency Injection library for Compose Multiplatform, Koin wrapper.

?? Cokoin Injection library for Compose (Multiplatform and Jetpack), Koin wrapper. It uses @Composable functions to configure KoinContext and Scopes.

Bruno Wieczorek 57 Dec 29, 2022
Mobile client for official Nextcloud News App written as Kotlin Multiplatform Project

Newsout Android and iOS mobile client for Nextcloud news App. The Android client is already available to download in the Play Store. F-Droid and Apple

Simon Schubert 118 Oct 3, 2022
Kotlin Multiplatform Application to show Crypto Coins

This is the codebase of Crypto currency Tracking Kotlin Multiplatform App. Components Shared Components Ktor (Network Client) SQL Delight (Local DB) A

Aman Bansal 10 Oct 31, 2022
BuildConfig for Kotlin Multiplatform Project

BuildKonfig BuildConfig for Kotlin Multiplatform Project. It currently supports embedding values from gradle file. Table Of Contents Motivation Usage

Yasuhiro SHIMIZU 331 Jan 4, 2023