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

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

Related

golang losing precision while converting float32 to float64?

In Golang, it seems that when a float64 var first convert to float32 then convert float64, it's value will change.
a := -8888.95
fmt.Println(a) // -8888.95
fmt.Println(float32(a)) // -8888.95
fmt.Println(float64(float32(a))) // -8888.9501953125
How can I make it unchanging
The way you have described the problem is perhaps misleading.
The precision is not lost "when converting float32 to float64"; rather, it is lost when converting from float64 to float32.
So how can you avoid losing precision when converting from float64 to float32? You can't. This task is impossible, and it's quite easy to see the reason why:
float64 has twice as many bits as float32
multiple different float64 values will map to the same float32 value due to the pigeonhole principle
the conversion is therefore not reversible.
package main
import (
"fmt"
)
func main() {
a := -8888.95
fmt.Printf("%.20f\n", a)
fmt.Printf("%.20f\n", float32(a))
fmt.Printf("%.20f\n", float64(float32(a)))
}
Adjusting your program to show a more precise output of each value, you'll see exactly where the precision is lost:
-8888.95000000000072759576
-8888.95019531250000000000
-8888.95019531250000000000
That is, after the float32 conversion (as is expected).
It's also worth noting that neither float64 nor float32 can represent your value -8888.95 exactly. If you convert this number to a fraction, you will get -177779/20. Notice the denominator, 20. The prime factorization of 20 is 2 * 2 * 5.
If you apply this process to a number and the prime factorization of the denominator contains any factors which are NOT 2, then you can rest assured that this number is definitely not representable exactly in binary floating point form. You may discover that the probability of any number passing this test is quite low.

Proper way for casting uint16 to int16 in Go

Bitwise manipulation and Go newbie here :D I am reading some data from sensor with Go and I get it as 2 bytes - let's say 0xFFFE. It is easy too cast it to uint16 since in Go we can just do uint16(0xFFFE) but what I need is to convert it to integer, because the sensor returns in fact values in range from -32768 to 32767. Now I thought "Maybe Go will be this nice and if I do int16(0xFFFE) it will understand what I want?", but no. I ended up using following solution (I translated some python code from web):
x := 0xFFFE
if (x & (1 << 15)) != 0 {
x = x - (1<<16)
}
It seems to work, but A) I am not entirely sure why, and B) It looks a bit ugly to what I imagined should be a trivial solution for casting uint16 to int16. Could anyone give me a hand and clarify why this is only way to do this? Or is there any other possible way?
But what you want works, "Go is nice":
ui := uint16(0xFFFE)
fmt.Println(ui)
i := int16(ui)
fmt.Println(i)
Output (try it on the Go Playground):
65534
-2
int16(0xFFFE) doesn't work because 0xfffe is an untyped integer constant which cannot be represented by a value of type int16, that's why the the compiler complains. But you can certainly convert any uint16 non-constant value to int16.
See possible duplicates:
Golang: on-purpose int overflow
Does go compiler's evaluation differ for constant expression and other expression

Comparing floats by ignoring last bit in golang

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/

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));

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