Is there any way to get square root of Nat numbers bigger than 64 bits?
I tried this but it produces error canister trapped explicitly: losing precision
var num = 112345678912345678900 * 112345678912345678900; // OK
var ftval = Float.fromInt(num); // <- error
var sqr = Float.sqrt(ftval);
Yes, that is rather unhelpful. I created an issue.
Until that’s fixed, you’ll have to use your own conversion routine, e.g.:
let base = 0x1000_0000_0000_0000;
func nontrappingNatToFloat(n : Nat) : Float {
var m : Nat = n;
var x : Float = 0;
var p : Float = 1;
while (m > 0) {
x += p * Float.fromInt(m % base);
p *= Float.fromInt(base);
m /= base;
};
return x;
};
That’s expected. 64 bit floats only have a mantissa precision of 53 bits, which corresponds to roughly 16 decimal digits. So you get rounding errors when you convert larger integers. See here for more background. Floats are inappropriate if you need exact precision.
It took its time, but after https://github.com/dfinity/motoko-base/pull/281 has been merged, the next release of Motoko will have non-trapping Float to Int (and back) conversions, as inherited from the underlying LibTomMath library. Still, many caveats apply, as round-tripping is lossy on many levels, and NaNs and Infinities will probably have surprising behaviours.
I use Newton iterative method to implement the code as follows:
public func sqrt(x : Nat) : Nat {
if (x == 0) {
return 0;
};
var pre : Int = 0;
var cur : Int = 1;
loop {
pre := cur;
cur := (cur + x/cur)/2;
if (Int.abs(cur - pre) <= 1) {
return Int.abs(cur);
};
} while(true);
Int.abs(cur);
};