• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

ionspin/kotlin-multiplatform-bignum: A Kotlin multiplatform library for arbitrar ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称(OpenSource Name):

ionspin/kotlin-multiplatform-bignum

开源软件地址(OpenSource Url):

https://github.com/ionspin/kotlin-multiplatform-bignum

开源编程语言(OpenSource Language):

Kotlin 99.7%

开源软件介绍(OpenSource Introduction):

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.6")

Snapshot builds

repositories {
    maven {
        url = uri("https://oss.sonatype.org/content/repositories/snapshots")
    }
}
implementation("com.ionspin.kotlin:bignum:0.3.7-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,


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap