Comparing floats by ignoring last bit in golang - go

A specification reads as follows:
It still considers real numbers equal if they differ in their last
binary digit.
I would like to implement this way of comparing floats for the float64 data type in Go. Unfortunately, the bitwise operators aren't defined for floating point numbers. Is there a way to achieve this way of comparing floats in the Go language?

This looks like a perfect use case for the following function from the math package:
func equal(x, y float64) bool {
return math.Nextafter(x, y) == y
}
Nextafter returns the next representable float64 value after x towards y.
Special cases are:
Nextafter(x, x) = x
Nextafter(NaN, y) = NaN
Nextafter(x, NaN) = NaN
https://play.golang.org/p/unRkkoe6wb

If you want to know if two float64 values are adjacent (that is, there's no float64 value between them):
func almostEqual(a, b float64) bool {
ai, bi := int64(math.Float64bits(a)), int64(math.Float64bits(b))
return a == b || -1 <= ai-bi && ai-bi <= 1
}
Mostly that's the same as saying they differ in the lowest bit of their mantissa.
This code doesn't work if a or b are NaNs, zeros or infinities, but you could add special cases if you wished.
See https://randomascii.wordpress.com/2012/01/23/stupid-float-tricks-2/

Related

Is NaN a comparable type in golang?

I came across a golang quiz which used NaN as a maps key and it ran without any error. If map's keys are supposed to be comparable is NaN a comparable type or is this a compiler bug which allowed NaN as a key.
Here's the quiz source, the go playground link and code below.
package main
var x = 0.0
func main() {
var a = x / x // a = NaN
var m = map[float64]int{a: 1}
m[a] = 2
for k := range m {
delete(m, k)
}
println(len(m)) // prints 2
}
Any float64 is comparable. There is no problem comparing any two floats. You can compare a NaN to 3.141 or NaN to NaN.
The problem is not comparability of NaNs/floats. The problem is that any comparison of a NaN with anything, even an other NaN or itself will result in false.
NaN != x for all x, including NaN
This makes using NaNs as map keys basically impossible because it's impossible to find a NaN.
The short answer is that this is not a compiler bug.
Maps require comparable types as keys. Float (regardless of float32 or float64) is a comparable type. What's odd about NaN (and in a different way, zero, which can be either positive or negative zero) is that while you can compare two NaNs, they always compare as unequal. So some float value x is not equal to itself when x is NaN. This produces surprises.
Issue 20660 comments note that the same kind of problems occur in a number of other languages.
I particularly like Russ Cox's comment that there are only bad answers here.

Is there a simple method for square root of big.Rat?

I need to find the square root of a big.Rat. Is there a way to do it without losing (already existing) accuracy?
For example, I could convert the numerator and denominator into floats, get the square root, and then convert it back...
func ratSquareRoot(num *big.Rat) *big.Rat {
f, exact := num.Float64() //Yuck! Floats!
squareRoot := math.Sqrt(f)
var accuracy int64 = 10 ^ 15 //Significant digits of precision for float64
return big.NewRat(int64(squareRoot*float64(accuracy)), accuracy)
// ^ This is now totally worthless. And also probably not simplified very well.
}
...but that would eliminate all of the accuracy of using a rational. Is there a better way of doing this?
The big.Float type has a .Sqrt(x) operation, and handles defining explicitly the precision you aim for. I'd try to use that and convert the result back to a Rat with the same operations in your question, only manipulating big.Int values.
r := big.NewRat(1, 3)
var x big.Float
x.SetPrec(30) // I didn't figure out the 'Prec' part correctly, read the docs more carefully than I did and experiement
x.SetRat(r)
var s big.Float
s.SetPrec(15)
s.Sqrt(&x)
r, _ = s.Rat(nil)
fmt.Println(x.String(), s.String())
fmt.Println(r.String(), float64(18919)/float64(32768))
playground

Integer functions of square and square root

Currently, math.Pow() and math.sqrt take float64 type arguments.
Do we have equivalent functions that take int type arguments?
If your return value is a float, you can use Ceil or Floor from the math package and then convert it to an int.
n := 5.5
o := math.Floor(n)
p := int(math.Pow(o, 2))
fmt.Println("Float:", n)
fmt.Println("Floor:", o)
fmt.Println("Square:", p)
5.5
5
25
Keep in mind that Floor still returns a float64, so you will still want to wrap it in int()
just create a float64 object using the int value. Example if int = 10.
var x float64 = 10
var b = math.Pow(2, x)
There are fast approximate algorithms described elsewhere on SO, such as this one. If performance is important, porting one of the C algorithms to Go might be worth the effort.
What you can do is type-cast a float to your value.
int a=10,b=2;
math.Pow(float(a),float(b));

GO: manipulating random generated float64

I was wondering if we can specify to the random generator to how many numbers should be generated after the point decimal?
Example of default behaviour:
fmt.Println(rand.float64())
Would print out the number 0.6046602879796196
Desired behaviour:
fmt.Println(rand.float64(4))
Would then print out the number 0.6047.
Does this functionality already exist in GO or would I have to implement it myself ?
Thank you!
It sounds like only the string representation is important to you, and the fmt package does provide that for you:
fmt.Printf("%1.4f", rand.Float64())
So yes, you would still need to wrap this call to specify the number of digits after the decimal point.
func RandomDigits(number int) string {
return fmt.Sprintf("%1." + strconv.Itoa(number) + "f", rand.Float64())
}
I don't know of such function, however it is easy to implement by yourself (play):
// Truncate the number x to n decimal places
//
// +- Inf -> +- Inf; NaN -> NaN
func truncate(x float64, n int) float64 {
return math.Trunc(x * math.Pow(10, float64(n))) * math.Pow(10, -float64(n))
}
Shift the number n decimal places to the left, truncate decimal places, shift the number n places to the right.
In case you want to present your number to the user then you will, at one point, convert the number
to a string. When you do that, you should not use this method and instead use string formatting as pointed
out by Tyson. For example, as floating point numbers are imprecise there might be rounding errors:
truncate(0.9405090880450124,3) // 0.9400000000000001

GoLang for loop with floats creates error

Can someone explain the following. I have a function in go that accepts a couple of float64 and then uses this value to calculate a lot of other values. The function looks like
func (g *Geometry) CalcStresses(x, zmax, zmin float64)(Vertical)
the result is put into a struct like
type Vertical struct {
X float64
Stresses []Stress
}
Now the funny thing is this. If I call the function like this;
for i:=14.0; i<15.0; i+=0.1{
result := geo.CalcStresses(i, 10, -10)
}
then I get a lot of results where the Stress array is empty, antoher interesting detail is that x sometimes shows like a number with a LOT of decimals (like 14.3999999999999999998)
However, if I call the function like this;
for i:=0; i<10; i++{
x := 14.0 + float64(i) * 0.1
result := geo.CalcStresses(x,10,-10)
}
then everything is fine.
Does anyone know why this happens?
Thanks in advance,
Rob
Not all real numbers can be represented precisely in binary floating point format, therefore looping over floating point number is asking for trouble.
From Wikipedia on Floating point
The fact that floating-point numbers cannot precisely represent all real numbers, and that floating-point operations cannot precisely represent true arithmetic operations, leads to many surprising situations. This is related to the finite precision with which computers generally represent numbers.
For example, the non-representability of 0.1 and 0.01 (in binary) means that the result of attempting to square 0.1 is neither 0.01 nor the representable number closest to it.
This code
for i := 14.0; i < 15.0; i += 0.1 {
fmt.Println(i)
}
produces this
14
14.1
14.2
14.299999999999999
14.399999999999999
14.499999999999998
14.599999999999998
14.699999999999998
14.799999999999997
14.899999999999997
14.999999999999996
You may use math.big.Rat type to represent rational numbers accurately.
Example
x := big.NewRat(14, 1)
y := big.NewRat(15, 1)
z := big.NewRat(1, 10)
for i := x; i.Cmp(y) < 0; i = i.Add(i, z) {
v, _ := i.Float64()
fmt.Println(v)
}

Resources