Cannot round to nearest .0001 - go

I have the following:
package main
import (
"fmt"
"math"
)
func main() {
nums := []float64{
0.15807659924030304, 0.10901273787021637, 0.04955724626779556, 0.05886702239513397,
}
for _, f := range nums {
fmt.Println(f, math.Round(f/.0001)*.0001)
}
}
output is:
0.15807659924030304 0.15810000000000002
0.10901273787021637 0.109
0.04955724626779556 0.049600000000000005
0.05886702239513397 0.0589
Why are some ending up with > 4 decimal places? How do I correct it?
https://play.golang.org/p/kLvVjmsjq6Y

fmt.Printf("%f %b %0.4f\n", f, f, math.Round(f/.0001)*.0001)
Try using that. The %0.4f will format it to 4 decimal places. In fact if you only need the string, use that instead of your Round function.
The %b will show you the real value. In Go that will display a large decimal (base-10) value, a p, then the exponent in powers of 2. So when it displays 5695309707476992p-55 you can find the floating point by doing 5695309707476992 / 2^55

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.

Is this strconv.ParseFloat() behavior expected or a bug? How can I get around it?

Run this code to see what I mean: Go Playground demo of issue
package main
import (
"fmt"
"strconv"
)
func main() {
// From https://golang.org/src/math/const.go
var SmallestNonzeroFloat64AsString string = "4.940656458412465441765687928682213723651e-324"
var SmallestNonzeroFloat64 float64
var err error
SmallestNonzeroFloat64, err = strconv.ParseFloat(SmallestNonzeroFloat64AsString, 64)
if err != nil {
panic(err)
}
fmt.Printf("SmallestNonzeroFloat64 = %g\n", SmallestNonzeroFloat64)
fmt.Printf("SmallestNonzeroFloat64 = %s\n", strconv.FormatFloat(SmallestNonzeroFloat64, 'f', -1, 64))
}
SmallestNonzeroFloat64 is defined in math/const.go and I assumed it can be represented by a float64 variable.
But when it is parsed into a float64 with strconv.ParseFloat() and printed with strconv.FormatFloat() the result is rounded.
Instead of 4.940656458412465441765687928682213723651e-324 I get 5e-324 (or its non-exponent equivalent, which you can see in the Go Playground results). The result is rounded.
Is there a way to get back the 4.940656458412465441765687928682213723651e-324?
Or is it a bug?
This is not a bug.
You could ask Go to print more digits.
fmt.Printf("SmallestNonzeroFloat64 = %.40g\n", SmallestNonzeroFloat64)
// 4.940656458412465441765687928682213723651e-324
However, 5e-324 and 4.94…e-324 are in fact the same value, so Go is not wrong printing 5e-324. This value (2-1074) is the smallest positive number representable by Float64 (also known as double in other languages). Larger numbers are all multiples of this, e.g. the next smallest number would be 2 × 2-1074 = 1e-323, the next would be 3 × 10-1074 = 1.5e-323, etc.
In the other words, all numbers more precise than 5e-324 would not be representable in Float64. So it makes no sense to print more digit after the "5". And 5e-324 is certainly more readable than 4.94…e-324.

How to read float notation in golang?

When printing out some values from a map of structs. I see certain float64 values with alternative notation. The test passes but how do you read this notation (4e-06). Is this value indeed the same as "0.000004"?
package main
import (
"fmt"
"strconv"
"testing"
)
func TestXxx(t *testing.T) {
num := fmt.Sprintf("%f", float64(1.225788)-float64(1.225784)) // 0.000004
f, _ := strconv.ParseFloat(num, 64)
if f == 0.000004 {
t.Log("Success")
} else {
t.Error("Not Equal", num)
}
if getFloat(f) == 0.000004 {
t.Log("Success")
}else{
t.Error("Fail", getFloat(f))
}
}
func getFloat(f float64) float64 {
fmt.Println("My Float:",f) // 4e-06
return f
}
The notation is called Scientific notation, and it is a convenient way to print very small or very large numbers in compact, short form.
It has the form of
m × 10n
(m times ten raised to the power of n)
In programming languages it is written / printed as:
men
See Spec: Floating-point literals.
Your number: 4e-06, where m=4 and n=-6, which means 4*10-6 which equals to 0.000004.
In order to print your floats in a regular way you can do something like this example:
package main
import (
"fmt"
"strconv"
)
func main() {
a, _ := strconv.ParseFloat("0.000004", 64)
b, _ := strconv.ParseFloat("0.0000000004", 64)
c := fmt.Sprintf("10.0004")
cc, _ := strconv.ParseFloat(c, 64)
fmt.Printf("%.6f\n", a) // 6 numbers after the point
fmt.Printf("%.10f\n", b) // 10 numbers afer the point
fmt.Printf("%.4f\n", cc) // 4 numbers after the point
}
Output:
0.000004
0.0000000004
10.0004
It is the same number. You can use fmt.Printf("My Float: %.6f\n",f) if you don't like the scientific notation. (This format requests that 6 digits will be printed after the decimal point.)

How to round to nearest int when casting float to int in go

When casting float to int the decimal is discarded. What's a clean way to cast so that it rounds to the nearest whole number instead.
x := int(3.6)
should equal 4 instead of 3.
int(f+0.5) will cause for it to round upwards if it's >= .5
You could use int(math.Round(f)) to round to the nearest whole number when converting a float to an int in Go. The decimal is also discarded when a float is set to a byte or a rune. Truncation doesn't happen when it's set to a string or a bool.
package main
import (
. "fmt"
. "math"
)
func main() {
f := 3.6
c := []interface{}{byte(f), f, int(Round(f)), rune(f), Sprintf("%.f", f), f != 0}
checkType(c)
}
func checkType(s []interface{}) {
for k, _ := range s {
Printf("%T %v\n", s[k], s[k])
}
}
Round returns the nearest integer, rounding half away from zero. See https://golang.org/pkg/math/#Round. See https://stackoverflow.com/a/61503758/12817546.
f := 3.6 truncates to “uint8 3”, f is “float64 3.6”, int(Round(f)) rounds up to “int 4”, rune(f) truncates to “int32 3”, Sprintf("%.f", f) is “string 3.6” and f != 0 outputs “bool true”.

How do I convert a float64 (stored as base-2) to a coefficient and exponent in base-10?

I'm looking for an algorithm that takes a 64-bit floating point number and outputs an exponent and coefficient so the input can be represented in the form float64 input = coefficient * 10 ^ exponent.
Supposedly this is 'not trivial', and many implementations of accurate decimal formats for Golang (which doesn't have a built-in decimal type) have some kind of hack for it like converting to a string and parsing that. While that solution does work in the packages I've seen, it just seems 'proper' to do it the mathematical/computer science-y way.
It may be not 100% precise, but you can use Log10:
package main
import (
"fmt"
"math"
)
func parts(v float64) (float64, int) {
e := math.Floor(math.Log10(v))
c := v / math.Pow(10, e)
return c, int(e)
}
func main() {
c, e := parts(1348.234e134)
fmt.Printf("%v * 10^%v", c, e) // 1.3482339999999997 * 10^137
}

Resources