Working with bitstrings and big.Int in Go - go

I'm new to Go and I'm working on a few exercises to get up to speed. How can I convert a string representing a sequence of bits to the appropriate datatype in Go?
For eg, I see that if its a bitstring representing a 64-bit number, I can do :-
val, err := strconv.ParseInt(bitstring, 2, 64)
However, if the bitstring represents a larger number(say 1024 or 2048 bits), how can I go about converting that number to the appropriate type in Go? I believe the type for managing big integers in Go is big.Int.

Yes, you may use the big.Int type, and its Int.SetString() method, passing 2 as the base.
Example:
i := big.NewInt(0)
if _, ok := i.SetString("10101010101010101010101010101010101010101010101010101010101010101010101010", 2); !ok {
fmt.Println("Invalid number!")
} else {
fmt.Println(i)
}
Output (try it on the Go playground):
12592977287652387236522

Related

how to force to save all decimal points of bigFloat in file, instead of rounding

Let's assume I have a very accurate input number(string format), and after math/big manipulation, I want to convert to string format again.
package main
import (
"fmt"
"math/big"
"os"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
// edit
s := "3.1415926123456789123456789123456789123456789"
var n, _ = new(big.Float).SetString(s)
// var n = big.NewFloat(3.1415926123456789123456789123456789123456789)
fmt.Println(n) // 3.1415926123456788
N := n.String()
fmt.Println(N) // 3.141592612
d1 := []byte(N)
err := os.WriteFile("./dat1.txt", d1, 0644) // 3.141592612
check(err)
}
How to save a big Float like 3.1415926123456789123456789123456789123456789 into a file? I want to keep all the decimal points, or at least as much as possible
You can parse and store your input "precisely", but you must increase the precision (the default precision doesn't cover that). Use Float.SetPrec() for that (requires a bit-count).
When generating text representation, use Float.Text(), again, with sufficiently large precision (requires a decimal digit-count). If you don't know the required digit-precision, as per the doc, you may use a negative value to have the smallest number of decimal digits that is needed for the Float's mantissa bits.
For example:
s := "3.1415926123456789123456789123456789123456789"
fmt.Println(s)
n := big.NewFloat(0)
n.SetPrec(200)
n.SetString(s)
N := n.Text('f', 50)
fmt.Println(N)
N = n.Text('f', -1)
fmt.Println(N)
This will output (try it on the Go Playground):
3.1415926123456789123456789123456789123456789
3.14159261234567891234567891234567891234567890000000
3.1415926123456789123456789123456789123456789
I've just looked what String method does and its documentation (https://pkg.go.dev/math/big#Float.String).
String formats x like x.Text('g', 10)...
So let's go to that method doc.
func (x *Float) Text(format byte, prec int) string
Text converts the floating-point number x to a string according to the given format and precision prec.
The precision prec controls the number of digits ... A negative precision selects the smallest number of decimal digits necessary to identify the value x uniquely
All you need is just reading the doc.

Golang string to uint

In my project, ID is designed as snowflakeid. The front end passes a string to me, and the database storage is bigint. This means that before I store it, it needs to be converted to uint. Please tell me what to do?
demo data :
m := "156343853366906880"
my code:
u, _ := strconv.ParseUint(m, 0, 19)
The expected results are accurate and will not lose accuracy
Third parameter of strconv.parseUint() is bitSize. 19 bits are not sufficient to represents the number 156343853366906880. So the method returns an error. (which you are ignoring by assigning it to _)
m := "156343853366906880"
_, err := strconv.ParseUint(m, 0, 19)
fmt.Println(err)
//strconv.ParseUint: parsing "156343853366906880": value out of range 524287
2^19 - 1 = 524287 is the biggest unsigned number that can be represented with 19 bits.
Pass 64 as bitSize :
m := "156343853366906880"
u, err := strconv.ParseUint(m, 0, 64)
if err == nil {
fmt.Print(u)
//156343853366906880
}
If your number is going to greater than uint64 use big.Int :
string to big Int in Go?
SnowflakeID is a time based 64-bit unique id. Since you need to convert string to a 64-bit number, strconv.ParseUint() is quite good. See, the reference at ParseUint.
In your code, you used 19 as bit size. Do not mix it with the number of digit(s) in the integer (unsigned) represented by the string from frontend.
For convert a 64-bit SnowflakeID (string) into a 64-bit unsigned integer, use 64 as bitSize arg.
U, err := strvonv.ParseUint(s, 0, 64)
if err != nil {
// handle error...
}
Also do not try to ignore error, when it really matters.

How to convert scientific notation string to a big.Int?

In golang I have a large number in a string like this: "4.49955000449955e+24".
How can I convert this to a big.Int?
use big.ParseFloat to get a big.Float and then the Int method to convert it to a big.Int:
flt, _, err := big.ParseFloat(input, 10, 0, big.ToNearestEven)
if err != nil {
/* handle a parsing error here */
}
var i = new(big.Int)
i, acc := flt.Int(i)
see it on the Go Playground.
By adjusting the prec argument to ParseFloat you can control the precision of the intermediate float — it takes at least 72 bits of precision for your example input to come out exactly (to a value ending in ten zeroes) but the default if you pass 0 is 64 bits.

Adding/subtracting two numeric strings

I have two variables with big numbers set as strings:
var numA = "340282366920938463463374607431768211456"
var numB = "17014118346046923173168730371588410572"
I want to be able to add and subtract these kinds of large string numbers in Go.
I know I need to use math/big but I still can not for the life of me figure out how, so any example help will be greatly appreciated!
You may use big.NewInt() to create a new big.Int value initialized with an int64 value. It returns you a pointer (*big.Int). Alternatively you could simply use the builtin new() function to allocate a big.Int value which will be 0 like this: new(big.Int), or since big.Int is a struct type, a simple composite literal would also do: &big.Int{}.
Once you have a value, you may use Int.SetString() to parse and set a number given as string. You can pass the base of the string number, and it also returns you a bool value indicating if parsing succeeded.
Then you may use Int.Add() and Int.Sub() to calculate the sum and difference of 2 big.Int numbers. Note that Add() and Sub() write the result into the receiver whose method you call, so if you need the numbers (operands) unchanged, use another big.Int value to calculate and store the result.
See this example:
numA := "340282366920938463463374607431768211456"
numB := "17014118346046923173168730371588410572"
ba, bb := big.NewInt(0), big.NewInt(0)
if _, ok := ba.SetString(numA, 10); !ok {
panic("invalid numA")
}
if _, ok := bb.SetString(numB, 10); !ok {
panic("invalid numB")
}
sum := big.NewInt(0).Add(ba, bb)
fmt.Println("a + b =", sum)
diff := big.NewInt(0).Sub(ba, bb)
fmt.Println("a - b =", diff)
Output (try it on the Go Playground):
a + b = 357296485266985386636543337803356622028
a - b = 323268248574891540290205877060179800884

How to generate a fixed length random number in Go?

What is the fastest and simplest way to generate fixed length random numbers in Go?
Say to generate 8-digits long numbers, the problem with rand.Intn(100000000) is that the result might be far less than 8-digits, and padding it with leading zeros doesn't look like a good answer to me.
I.e., I care about the the quality of the randomness more in the sense of its length. So I'm thinking, for this specific problem, would the following be the fastest and simplest way to do it?
99999999 - rand.Int63n(90000000)
I.e., I guess Int63n might be better for my case than Intn. Is it ture, or it is only a wishful thinking? Regarding randomness of the full 8-digits, would the two be the same, or there is really one better than the other?
Finally, any better way than above?
UPDATE:
Please do not provide low + rand(hi-low) as the answer, as everyone knows that. It is equivalent of what I'm doing now, and it doesn't answer my real question, "Regarding randomness of the full 8-digits, would the two be the same, or there is really one better than the other? "
If nobody can answer that, I'll plot a 2-D scatter plot between the two and find out myself...
Thanks
This is a general purpose function for generating numbers within a range
func rangeIn(low, hi int) int {
return low + rand.Intn(hi-low)
}
See it on the Playground
In your specific case, trying to generate 8 digit numbers, the range would be (10000000, 99999999)
It depend on value range you want to use.
If you allow value range [0-99999999] and padding zero ip number of char < 8, then use fmt like fmt.Sprintf("%08d",rand.Intn(100000000)).
If you dont want padding, which value in range [10000000, 99999999], then give it a base like ranNumber := 10000000 + rand.Intn(90000000)`
See it on Playground
crypto/rand package is used to generate number.
func generateRandomNumber(numberOfDigits int) (int, error) {
maxLimit := int64(int(math.Pow10(numberOfDigits)) - 1)
lowLimit := int(math.Pow10(numberOfDigits - 1))
randomNumber, err := rand.Int(rand.Reader, big.NewInt(maxLimit))
if err != nil {
return 0, err
}
randomNumberInt := int(randomNumber.Int64())
// Handling integers between 0, 10^(n-1) .. for n=4, handling cases between (0, 999)
if randomNumberInt <= lowLimit {
randomNumberInt += lowLimit
}
// Never likely to occur, kust for safe side.
if randomNumberInt > int(maxLimit) {
randomNumberInt = int(maxLimit)
}
return randomNumberInt, nil
}
I recently needed to do something like this, but with a certain byte length (rather than number of digits) and with numbers larger than max int64 (so using math/big.Int). Here was my general solution:
See on the Playground (with added code comments)
func generateRandomBigInt(numBytes int) (*big.Int, error) {
value := make([]byte, numBytes)
_, err := rand.Reader.Read(value)
if err != nil {
return nil, err
}
for true {
if value[0] != 0 {
break
}
firstByte := value[:1]
_, err := rand.Reader.Read(firstByte)
if err != nil {
return nil, err
}
}
return (&big.Int{}).SetBytes(value), nil
}

Resources