From b38ff2b29f7ae80cb05fe01ac3acfc262514cdae Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 26 Jul 2022 17:58:06 -0500 Subject: [PATCH 1/4] Implement toBiggestFloat Fixes Feature request: toBiggestInt #34 --- src/bigints.nim | 7 ++++++- tests/tbigints.nim | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/bigints.nim b/src/bigints.nim index 9ef5abb..21e0226 100644 --- a/src/bigints.nim +++ b/src/bigints.nim @@ -1,6 +1,6 @@ ## Arbitrary precision integers. -import std/[algorithm, bitops, math, options] +import std/[algorithm, bitops, fenv, math, options] type BigInt* = object @@ -1140,6 +1140,11 @@ func fastLog2*(a: BigInt): int = return -1 bitops.fastLog2(a.limbs[^1]) + 32*(a.limbs.high) +func toBiggestFloat*(x: BigInt): BiggestFloat = + let l = mantissaDigits(BiggestFloat)+1 + let shift = max(fastLog2(x) - l, 0) + let mantissa = BiggestFloat(toInt[BiggestInt](x shr shift).get()) + result = mantissa * pow(2.0, BiggestFloat(shift)) func invmod*(a, modulus: BigInt): BigInt = ## Compute the modular inverse of `a` modulo `modulus`. diff --git a/tests/tbigints.nim b/tests/tbigints.nim index 3b3a59d..d3f8be9 100644 --- a/tests/tbigints.nim +++ b/tests/tbigints.nim @@ -786,6 +786,16 @@ proc main() = doAssert pred(a, 3) == initBigInt(4) doAssert succ(a, 3) == initBigInt(10) + block: # to float + doAssert toBiggestFloat(initBigInt("0")) == 0.0 + doAssert toBiggestFloat(initBigInt("1")) == 1.0 + doAssert toBiggestFloat(initBigInt("-1")) == -1.0 + doAssert toBiggestFloat(initBigInt(BiggestInt.high)) == BiggestInt.high.toBiggestFloat + doAssert toBiggestFloat(initBigInt(BiggestInt.low)) == BiggestInt.low.toBiggestFloat + doAssert toBiggestFloat(initBigInt(BiggestInt.high) * initBigInt(4)) == BiggestInt.high.toBiggestFloat * 4.0 + doAssert toBiggestFloat(initBigInt(BiggestInt.low) * initBigInt(4)) == BiggestInt.low.toBiggestFloat * 4.0 + doAssert toBiggestFloat(initBigInt("17976931348623157") * initBigInt(10).pow(292)) == 17976931348623157e292 + static: main() main() From 6c73172d438511b1b0b14816a0d25ef1a38e48a3 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 27 Jul 2022 13:00:00 -0500 Subject: [PATCH 2/4] don't use BiggestInt in implementation of toBiggestFloat --- src/bigints.nim | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/bigints.nim b/src/bigints.nim index 21e0226..8fb374a 100644 --- a/src/bigints.nim +++ b/src/bigints.nim @@ -1141,10 +1141,11 @@ func fastLog2*(a: BigInt): int = bitops.fastLog2(a.limbs[^1]) + 32*(a.limbs.high) func toBiggestFloat*(x: BigInt): BiggestFloat = - let l = mantissaDigits(BiggestFloat)+1 - let shift = max(fastLog2(x) - l, 0) - let mantissa = BiggestFloat(toInt[BiggestInt](x shr shift).get()) - result = mantissa * pow(2.0, BiggestFloat(shift)) + let lolimb = max(x.limbs.low, x.limbs.high - 1 - mantissaDigits(BiggestFloat) div 32) + for i in lolimb..x.limbs.high: + result += x.limbs[i].int64.toBiggestFloat * pow(2.0, 32.0 * i.float) + if x.isNegative: + result = -result func invmod*(a, modulus: BigInt): BigInt = ## Compute the modular inverse of `a` modulo `modulus`. From b08bbbeb940882a10b3f8b8b9fd22b7528f0b6e5 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 27 Jul 2022 13:21:00 -0500 Subject: [PATCH 3/4] Add tests for infinity --- tests/tbigints.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/tbigints.nim b/tests/tbigints.nim index d3f8be9..5c24c02 100644 --- a/tests/tbigints.nim +++ b/tests/tbigints.nim @@ -795,6 +795,9 @@ proc main() = doAssert toBiggestFloat(initBigInt(BiggestInt.high) * initBigInt(4)) == BiggestInt.high.toBiggestFloat * 4.0 doAssert toBiggestFloat(initBigInt(BiggestInt.low) * initBigInt(4)) == BiggestInt.low.toBiggestFloat * 4.0 doAssert toBiggestFloat(initBigInt("17976931348623157") * initBigInt(10).pow(292)) == 17976931348623157e292 + doAssert toBiggestFloat(initBigInt("-10").pow(400)) == Inf + doAssert toBiggestFloat(initBigInt("-10").pow(401)) == -Inf + static: main() From 7fe47adc1842cb31607c386fea621c956439583e Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 28 Jul 2022 08:52:18 -0500 Subject: [PATCH 4/4] support for other floating point types --- src/bigints.nim | 6 +++--- tests/tbigints.nim | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/bigints.nim b/src/bigints.nim index 8fb374a..fe1498c 100644 --- a/src/bigints.nim +++ b/src/bigints.nim @@ -1140,10 +1140,10 @@ func fastLog2*(a: BigInt): int = return -1 bitops.fastLog2(a.limbs[^1]) + 32*(a.limbs.high) -func toBiggestFloat*(x: BigInt): BiggestFloat = - let lolimb = max(x.limbs.low, x.limbs.high - 1 - mantissaDigits(BiggestFloat) div 32) +func to*(x: BigInt, T:type SomeFloat): T = + let lolimb = max(x.limbs.low, x.limbs.high - 1 - mantissaDigits(T) div 32) for i in lolimb..x.limbs.high: - result += x.limbs[i].int64.toBiggestFloat * pow(2.0, 32.0 * i.float) + result += T(x.limbs[i].int64) * pow(2.0, 32.0 * T(i)) if x.isNegative: result = -result diff --git a/tests/tbigints.nim b/tests/tbigints.nim index 5c24c02..c3fd267 100644 --- a/tests/tbigints.nim +++ b/tests/tbigints.nim @@ -787,18 +787,34 @@ proc main() = doAssert succ(a, 3) == initBigInt(10) block: # to float - doAssert toBiggestFloat(initBigInt("0")) == 0.0 - doAssert toBiggestFloat(initBigInt("1")) == 1.0 - doAssert toBiggestFloat(initBigInt("-1")) == -1.0 - doAssert toBiggestFloat(initBigInt(BiggestInt.high)) == BiggestInt.high.toBiggestFloat - doAssert toBiggestFloat(initBigInt(BiggestInt.low)) == BiggestInt.low.toBiggestFloat - doAssert toBiggestFloat(initBigInt(BiggestInt.high) * initBigInt(4)) == BiggestInt.high.toBiggestFloat * 4.0 - doAssert toBiggestFloat(initBigInt(BiggestInt.low) * initBigInt(4)) == BiggestInt.low.toBiggestFloat * 4.0 - doAssert toBiggestFloat(initBigInt("17976931348623157") * initBigInt(10).pow(292)) == 17976931348623157e292 - doAssert toBiggestFloat(initBigInt("-10").pow(400)) == Inf - doAssert toBiggestFloat(initBigInt("-10").pow(401)) == -Inf - - + doAssert initBigInt("1").to(float32) is float32 + doAssert initBigInt("1").to(float64) is float64 + doAssert initBigInt("1").to(BiggestFloat) is BiggestFloat + doAssert initBigInt("1").to(float) is float + + doAssert initBigInt("0").to(BiggestFloat) == 0.0 + doAssert initBigInt("1").to(BiggestFloat) == 1.0 + doAssert initBigInt("1").to(float32) == 1.0 + doAssert initBigInt("1").to(float64) == 1.0 + + doAssert initBigInt("-1").to(BiggestFloat) == -1.0 + doAssert initBigInt(BiggestInt.high).to(BiggestFloat) == BiggestFloat(BiggestInt.high) + doAssert initBigInt(BiggestInt.low).to(BiggestFloat) == BiggestFloat(BiggestInt.low) + doAssert (initBigInt(BiggestInt.high) * initBigInt(4)).to(BiggestFloat) == BiggestFloat(BiggestInt.high) * 4.0 + doAssert (initBigInt(BiggestInt.low) * initBigInt(4)).to(BiggestFloat) == BiggestFloat(BiggestInt.low) * 4.0 + doAssert (initBigInt("17976931348623157") * initBigInt(10).pow(292)).to(BiggestFloat) == 17976931348623157e292 + + # limits of exact representability + doAssert initBigInt(1 shl 24).to(float32) == float32(1 shl 24) + doAssert initBigInt(-1 shl 24).to(float32) == float32(-1 shl 24) + doAssert initBigInt(1 shl 53).to(float64) == float64(1 shl 53) + doAssert initBigInt(-1 shl 53).to(float64) == float64(-1 shl 53) + # out of exact integer representation range + doAssert initBigInt(int32.high).to(float32) == float32(int32.high) + doAssert initBigInt(int64.high).to(float64) == float64(int64.high) + # well out out of finite range + doAssert initBigInt("-10").pow(400).to(float64) == Inf + doAssert initBigInt("-10").pow(401).to(float64) == -Inf static: main() main()