big.Float SetPrec weird behaviour - go

After doing some calculations using big.Float in golang, I am setting the precision to 2.
And even thou the number is just a simple 10, after setting the precision it is 8.
package main
import (
"fmt"
"math/big"
)
func main() {
cost := big.NewFloat(10)
fmt.Println("COST NOW", cost)
perKWh := big.NewFloat(0)
cost.Add(cost, perKWh)
fmt.Println("COST ", cost.String())
perMinute := big.NewFloat(0)
cost.Add(cost, perMinute)
fmt.Println("COST ", cost.String())
discountAmount := big.NewFloat(0)
cost.Sub(cost, discountAmount)
floatCos, _ := cost.Float64()
fmt.Println(fmt.Sprintf("COST FLOAT %v", floatCos))
cost.SetPrec(2)
fmt.Println("COST ", cost.String())
}
Check playground example here: https://play.golang.org/p/JmCRXkD5u49
Would like to understand why

From the fine manual:
type Float
[...]
Each Float value also has a precision, rounding mode, and accuracy. The precision is the maximum number of mantissa bits available to represent the value. The rounding mode specifies how a result should be rounded to fit into the mantissa bits, and accuracy describes the rounding error with respect to the exact result.
And big.Float is represented internally as:
sign × mantissa × 2**exponent
When you call SetPrec you're setting the number of bits available for the mantissa, not the number of digits of precision in the decimal representation of the number.
You can't represent decimal 10 (1010 binary) in two bits of mantissa so it rounds to decimal 8 (1000 binary) which can fit into 2 bits. You need at least three bits to store the 101 part of decimal 10. 8 can fit into a single bit of mantissa so you'll see the same 8 if you say cost.SetPrec(1).
You need to be thinking in terms of binary when using the big package.

First, discard all the irrelevant code. Next, print useful diagnostic information.
package main
import (
"fmt"
"math/big"
)
func main() {
cost := big.NewFloat(10)
fmt.Println("Cost ", cost.String())
fmt.Println("Prec", cost.Prec())
fmt.Println("MinPrec", cost.MinPrec())
fmt.Println("Mode", cost.Mode())
cost.SetPrec(2)
fmt.Println("Prec", cost.Prec())
fmt.Println("Accuracy", cost.Acc())
fmt.Println("Cost ", cost.String())
}
Output:
Cost 10
Prec 53
MinPrec 3
Mode ToNearestEven
Prec 2
Accuracy Below
Cost 8
Round 10 to the nearest even number that can be represented in an sign, exponent, and a 2-bit mantissa and you get 8.
Rounding ToNearestEven is IEE754 rounding. Round to nearest, ties to even – rounds to the nearest value; if the number falls midway it is rounded to the nearest value with an even (zero) least significant bit.

Related

Go/Golang: how to extract least significant digits from big.Float?

In Go/Golang I have a variable of type big.Float with an (arbitrary) precision of 3,324,000 to represent a decimal number of 1,000,000 digits. It's the result of an iteration to calculate pi.
Now I want to print out the least significant 100 digits, i.e. digits 999,900 to 1,000,000.
I tried to convert the variable to a string by using fmt.Sprintf() and big.Text(). However, both functions consume a lot of processing time which gets unacceptable (many hours and even days) when further raising the precision.
I'm searching for some functions which extract the last 100 (decimal) digits of the variable.
Thanks in advance for your kind support.
The standard library doesn't provide a function to return those digits efficiently, but you can calculate them.
It is more efficient to isolate the digits you are interested in and print them. This avoids excessive calculations of an extremely large number to determine each individual digit.
The code below shows a way it can be done. You will need to ensure you have enough precision to generate them accurately.
package main
import (
"fmt"
"math"
"math/big"
)
func main() {
// Replace with larger calculation.
pi := big.NewFloat(math.Pi)
const (
// Pi: 3.1415926535897932...
// Output: 5926535897
digitOffset = 3
digitLength = 10
)
// Move the desired digits to the right side of the decimal point.
mult := pow(10, digitOffset)
digits := new(big.Float).Mul(pi, mult)
// Remove the integer component.
digits.Sub(digits, trunc(digits))
// Move the digits to the left of the decimal point, and truncate
// to an integer representing the desired digits.
// This avoids undesirable rounding if you simply print the N
// digits after the decimal point.
mult = pow(10, digitLength)
digits.Mul(digits, mult)
digits = trunc(digits)
// Display the next 'digitLength' digits. Zero padded.
fmt.Printf("%0*.0f\n", digitLength, digits)
}
// trunc returns the integer component.
func trunc(n *big.Float) *big.Float {
intPart, accuracy := n.Int(nil)
_ = accuracy
return new(big.Float).SetInt(intPart)
}
// pow calculates n^idx.
func pow(n, idx int64) *big.Float {
if idx < 0 {
panic("invalid negative exponent")
}
result := new(big.Int).Exp(big.NewInt(n), big.NewInt(idx), nil)
return new(big.Float).SetInt(result)
}

Conditional formatting of integers using decimal places

I have the following situation: I'll be receiving integers and have to format them according to the following rules:
10000 -> 100 // removing the last "00"
10010 -> 100.1 // removing the last "0", and adding a decimal place
10011 -> 100.11 // adding two decimal places
How this can be done? Thanks so much in advance.
Using floating point numbers
Convert the integer number to float64, divide it by 100 and use the %g verb of the fmt package, it removes trailing zeros:
For floating-point values, width sets the minimum width of the field and precision sets the number of places after the decimal, if appropriate, except that for %g/%G precision sets the maximum number of significant digits (trailing zeros are removed).
To avoid "large" numbers reverting to %e scientific notation (numbers with more than the default precision which is 6 for %g), specify the width explicitly, something like this:
fmt.Printf("%.12g\n", float64(v)/100)
Testing it:
for _, v := range []int{
10000, 10010, 10011,
10000000, 10000010, 10000011,
10000000000, 10000000010, 10000000011,
} {
fmt.Printf("%.12g\n", float64(v)/100)
}
This will output (try it on the Go Playground):
100
100.1
100.11
100000
100000.1
100000.11
100000000
100000000.1
100000000.11
Using integers
Without converting to floating point numbers (and relying on the trailing zero removal of %g), this is how you could do it using integer arithmetic:
The last 2 digits are the remainder of dividing by 100, the rest is the result of integer division by 100. You can format these 2 numbers depending on the remainder like this:
switch q, r := v/100, v%100; {
case r == 0:
fmt.Println(q)
case r%10 == 0:
fmt.Printf("%d.%d\n", q, r/10)
default:
fmt.Printf("%d.%02d\n", q, r)
}
Try this one on the Go Playground.

Max value on 64 bit

Below code compiles:
package main
import "fmt"
const (
// Max integer value on 64 bit architecture.
maxInt = 9223372036854775807
// Much larger value than int64.
bigger = 9223372036854775808543522345
// Will NOT compile
// biggerInt int64 = 9223372036854775808543522345
)
func main() {
fmt.Println("Will Compile")
//fmt.Println(bigger) // error
}
Type is size in memory + representation of bits in that memory
What is the implicit type assigned to bigger at compile time? Because error constant 9223372036854775808543522345 overflows int for line fmt.Println(bigger)
Those are untyped constants. They have larger limits than typed constants:
https://golang.org/ref/spec#Constants
In particular:
Represent integer constants with at least 256 bits.
None, it's an untyped constant. Because you haven't assigned it to any variable or used it in any expression, it doesn't "need" to be given a representation as any concrete type yet. Numeric constants in Go have effectively unlimited precision (required by the language spec to be at least 256 bits for integers, and at least 256 mantissa bits for floating-point numbers, but I believe that the golang/go compiler uses the Go arbitrary-precision types internally which are only limited by memory). See the section about Constants in the language spec.
What is the use of a constant if you can't assign it to a variable of any type? Well, it can be part of a constant expression. Constant expressions are evaluated at arbitrary precision, and their results may be able to be assigned to a variable. In other words, it's allowed to use values that are too big to represent to reach an answer that is representable, as long as all of that computation happens at compile time.
From this comment:
my goal is to convertf bigger = 9223372036854775808543522345 to binary form
we find that your question is an XY problem.
Since we do know that the constant exceeds 64 bits, we'll need to take it apart into multiple 64-bit words, or store it in some sort of bigger-integer storage.
Go provides math/big for general purpose large-number operations, or in this case we can take advantage of the fact that it's easy to store up to 127-bit signed values (or 128-bit unsigned values) in a struct holding two 64-bit integers (at least one of which is unsigned).
This rather trivial program prints the result of converting to binary:
500000000 x 2-sup-64 + 543522345 as binary:
111011100110101100101000000000000000000000000000000000000000000100000011001010111111000101001
package main
import "fmt"
const (
// Much larger value than int64.
bigger = 9223372036854775808543522345
d64 = 1 << 64
)
type i128 struct {
Upper int64
Lower uint64
}
func main() {
x := i128{Upper: bigger / d64, Lower: bigger % d64}
fmt.Printf("%d x 2-sup-64 + %d as binary:\n%b%.64b\n", x.Upper, x.Lower, x.Upper, x.Lower)
}

Rounding value half-up to 2 decimal places in Go

Rounding positive value (example here: 1.015) half-up to 2 decimal places using math.Round() in Go:
fmt.Println(math.Round(1.015*100) / 100)
Go Playground
I got: 1.02. That's correct.
But when I employed a function to do the same job:
func RoundHalfUp(x float64) float64 {
return math.Round(x*100) / 100
}
Go Playground
I got 1.01.
What's wrong with the RoundHalfUp function?
The Go Programming Language
Specification
Constants
Numeric constants represent exact values of arbitrary precision and do
not overflow.
Implementation restriction: Although numeric constants have arbitrary
precision in the language, a compiler may implement them using an
internal representation with limited precision. That said, every
implementation must:
Represent floating-point constants, including the parts of a complex constant, with a mantissa of at least 256 bits and a signed
binary exponent of at least 16 bits.
Round to the nearest representable constant if unable to represent a floating-point or complex constant due to limits on precision.
These requirements apply both to literal constants and to the result
of evaluating constant expressions.
Constant expressions
Constant expressions may contain only constant operands and are
evaluated at compile time.
Constant expressions are always evaluated exactly; intermediate values
and the constants themselves may require precision significantly
larger than supported by any predeclared type in the language.
Implementation restriction: A compiler may use rounding while
computing untyped floating-point or complex constant expressions; see
the implementation restriction in the section on constants. This
rounding may cause a floating-point constant expression to be invalid
in an integer context, even if it would be integral when calculated
using infinite precision, and vice versa.
Implement the RoundHalfUp function like the Go compiler does for math.Round(1.015*100) / 100. 1.015*100 is a untyped floating-point constant expression. Use the math/big package with at least 256 bits of precision. Go float64 (IEEE-754 64-bit floating-point) has 53 bits of precision.
For example, with 256 bits of precision (constant expression),
package main
import (
"fmt"
"math"
"math/big"
)
func RoundHalfUp(x string) float64 {
// math.Round(x*100) / 100
xf, _, err := big.ParseFloat(x, 10, 256, big.ToNearestEven)
if err != nil {
panic(err)
}
xf100, _ := new(big.Float).Mul(xf, big.NewFloat(100)).Float64()
return math.Round(xf100) / float64(100)
}
func main() {
fmt.Println(RoundHalfUp("1.015"))
}
Playground: https://play.golang.org/p/uqtYwP4o22B
Output:
1.02
If we only use 53 bits of precision (float64):
xf, _, err := big.ParseFloat(x, 10, 53, big.ToNearestEven)
Playground: https://play.golang.org/p/ejz-wkuycaU
Output:
1.01

Is there any standard library to convert float64 to string with fix width with maximum number of significant digits?

Imagine for printing in a 12 fixed width table we need printing float64 numbers:
fmt.Printf("%12.6g\n", 9.405090880450127e+119) //"9.40509e+119"
fmt.Printf("%12.6g\n", 0.1234567890123) //" 0.123457"
fmt.Printf("%12.6g\n", 123456789012.0) //" 1.23457e+11"
We prefer 0.1234567890 to " 0.123457" we lose 6 significant digits.
We prefer 123456789012 to " 1.23457e+11" we lose 6 significant digits.
Is there any standard library to convert float64 to string with fix width with maximum number of significant digits?
Thanks in Advance.
Basically you have 2 output formats: either a scientific notation or a regular form. The turning point between those 2 formats is 1e12.
So you can branch if x >= 1e12. In both branches you may do a formatting with 0 fraction digits to see how long the number will be, so you can calculate how many fraction digits will fit in for 12 width, and so you can construct the final format string, using the calculated precision.
The pre-check is required in the scientific notation too (%g), because the width of exponent may vary (e.g. e+1, e+10, e+100).
Here is an example implementation. This is to get you started, but it does not mean to handle all cases, and it is not the most efficient solution (but relatively simple and does the job):
// format12 formats x to be 12 chars long.
func format12(x float64) string {
if x >= 1e12 {
// Check to see how many fraction digits fit in:
s := fmt.Sprintf("%.g", x)
format := fmt.Sprintf("%%12.%dg", 12-len(s))
return fmt.Sprintf(format, x)
}
// Check to see how many fraction digits fit in:
s := fmt.Sprintf("%.0f", x)
if len(s) == 12 {
return s
}
format := fmt.Sprintf("%%%d.%df", len(s), 12-len(s)-1)
return fmt.Sprintf(format, x)
}
Testing it:
fs := []float64{0, 1234.567890123, 0.1234567890123, 123456789012.0, 1234567890123.0,
9.405090880450127e+9, 9.405090880450127e+19, 9.405090880450127e+119}
for _, f := range fs {
fmt.Println(format12(f))
}
Output (try it on the Go Playground):
0.0000000000
0.1234567890
1234.5678901
123456789012
1.234568e+12
9405090880.5
9.405091e+19
9.40509e+119

Resources