Function toFloat [src]
Return a floating-point value that is the closest value to a Rational.
The result may not be exact if the Rational is too precise or too large for the
target type.
Prototype
pub fn toFloat(self: Rational, comptime T: type) !T
Parameters
self: Rational
T: type
Source
pub fn toFloat(self: Rational, comptime T: type) !T {
// Translated from golang.go/src/math/big/rat.go.
// TODO: Indicate whether the result is not exact.
debug.assert(@typeInfo(T) == .float);
const fsize = @typeInfo(T).float.bits;
const BitReprType = std.meta.Int(.unsigned, fsize);
const msize = math.floatMantissaBits(T);
const msize1 = msize + 1;
const msize2 = msize1 + 1;
const esize = math.floatExponentBits(T);
const ebias = (1 << (esize - 1)) - 1;
const emin = 1 - ebias;
if (self.p.eqlZero()) {
return 0;
}
// 1. left-shift a or sub so that a/b is in [1 << msize1, 1 << (msize2 + 1)]
var exp = @as(isize, @intCast(self.p.bitCountTwosComp())) - @as(isize, @intCast(self.q.bitCountTwosComp()));
var a2 = try self.p.clone();
defer a2.deinit();
var b2 = try self.q.clone();
defer b2.deinit();
const shift = msize2 - exp;
if (shift >= 0) {
try a2.shiftLeft(&a2, @as(usize, @intCast(shift)));
} else {
try b2.shiftLeft(&b2, @as(usize, @intCast(-shift)));
}
// 2. compute quotient and remainder
var q = try Int.init(self.p.allocator);
defer q.deinit();
// unused
var r = try Int.init(self.p.allocator);
defer r.deinit();
try Int.divTrunc(&q, &r, &a2, &b2);
var mantissa = extractLowBits(q, BitReprType);
var have_rem = r.len() > 0;
// 3. q didn't fit in msize2 bits, redo division b2 << 1
if (mantissa >> msize2 == 1) {
if (mantissa & 1 == 1) {
have_rem = true;
}
mantissa >>= 1;
exp += 1;
}
if (mantissa >> msize1 != 1) {
// NOTE: This can be hit if the limb size is small (u8/16).
@panic("unexpected bits in result");
}
// 4. Rounding
if (emin - msize <= exp and exp <= emin) {
// denormal
const shift1 = @as(math.Log2Int(BitReprType), @intCast(emin - (exp - 1)));
const lost_bits = mantissa & ((@as(BitReprType, @intCast(1)) << shift1) - 1);
have_rem = have_rem or lost_bits != 0;
mantissa >>= shift1;
exp = 2 - ebias;
}
// round q using round-half-to-even
var exact = !have_rem;
if (mantissa & 1 != 0) {
exact = false;
if (have_rem or (mantissa & 2 != 0)) {
mantissa += 1;
if (mantissa >= 1 << msize2) {
// 11...1 => 100...0
mantissa >>= 1;
exp += 1;
}
}
}
mantissa >>= 1;
const f = math.scalbn(@as(T, @floatFromInt(mantissa)), @as(i32, @intCast(exp - msize1)));
if (math.isInf(f)) {
exact = false;
}
return if (self.p.isPositive()) f else -f;
}