Merge pull request #102 from MovingtoMars/float_print

float printing mostly works
This commit is contained in:
Andrew Kelley 2016-01-29 11:20:34 -07:00
commit 580df2f530
2 changed files with 143 additions and 1 deletions

View File

@ -17,7 +17,11 @@ pub fn f64_get_neg_inf() -> f64 {
}
pub fn f64_is_nan(f: f64) -> bool {
0x7FFFFFFFFFFFFFFF == f64_to_bits(f) // TODO improve to catch all cases
const bits = f64_to_bits(f);
const exp: i64 = i64((bits >> 52) & ((1 << 11) - 1));
const sig = (bits & ((1 << 52) - 1)) | (1 << 52);
sig != 0 && exp == (1 << 11) - 1
}
pub fn f64_is_inf(f: f64) -> bool {

View File

@ -108,6 +108,20 @@ pub struct OutStream {
return amt_printed;
}
pub fn print_f64(os: &OutStream, x: f64) -> %isize {
if (os.index + max_f64_digits >= os.buffer.len) {
%return os.flush();
}
const amt_printed = buf_print_f64(os.buffer[os.index...], x, 4);
os.index += amt_printed;
if (!os.buffered) {
%return os.flush();
}
return amt_printed;
}
pub fn flush(os: &OutStream) -> %void {
const amt_written = write(os.fd, os.buffer.ptr, os.index);
os.index = 0;
@ -229,6 +243,130 @@ pub fn buf_print_u64(out_buf: []u8, x: u64) -> isize {
return len;
}
pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize {
const numExpBits = 11;
const numRawSigBits = 52; // not including implicit 1 bit
const expBias = 1023;
var decs = decimals;
if (decs >= max_u64_base10_digits) {
decs = max_u64_base10_digits - 1;
}
if (x == f64_get_pos_inf()) {
const buf2 = "+Inf";
@memcpy(&out_buf[0], &buf2[0], buf2.len);
return 4;
} else if (x == f64_get_neg_inf()) {
const buf2 = "-Inf";
@memcpy(&out_buf[0], &buf2[0], buf2.len);
return 4;
} else if (f64_is_nan(x)) {
const buf2 = "NaN";
@memcpy(&out_buf[0], &buf2[0], buf2.len);
return 3;
}
var buf: [max_f64_digits]u8 = undefined;
var len: isize = 0;
// 1 sign bit
// 11 exponent bits
// 52 significand bits (+ 1 implicit always non-zero bit)
const bits = f64_to_bits(x);
if (bits & (1 << 63) != 0) {
buf[0] = '-';
len += 1;
}
const rexponent: i64 = i64((bits >> numRawSigBits) & ((1 << numExpBits) - 1));
const exponent = rexponent - expBias - numRawSigBits;
if (rexponent == 0) {
buf[len] = '0';
len += 1;
@memcpy(&out_buf[0], &buf[0], len);
return len;
}
const sig = (bits & ((1 << numRawSigBits) - 1)) | (1 << numRawSigBits);
if (exponent >= 0) {
// number is an integer
if (exponent >= 64 - 53) {
// use XeX form
// TODO support printing large floats
//len += buf_print_u64(buf[len...], sig << 10);
const str = "LARGEF64";
@memcpy(&buf[len], &str[0], str.len);
len += str.len;
} else {
// use typical form
len += buf_print_u64(buf[len...], sig << u64(exponent));
buf[len] = '.';
len += 1;
var i: isize = 0;
while (i < decs) {
buf[len] = '0';
len += 1;
i += 1;
}
}
} else {
// number is not an integer
// print out whole part
len += buf_print_u64(buf[len...], sig >> u64(-exponent));
buf[len] = '.';
len += 1;
// print out fractional part
// dec_num holds: fractional part * 10 ^ decs
var dec_num: u64 = 0;
var a: isize = 1;
var i: isize = 0;
while (i < decs + 5) {
a *= 10;
i += 1;
}
// create a mask: 1's for the fractional part, 0's for whole part
var masked_sig = sig & ((1 << u64(-exponent)) - 1);
i = -1;
while (i >= exponent) {
var bit_set = ((1 << u64(i-exponent)) & masked_sig) != 0;
if (bit_set) {
dec_num += usize(a) >> usize(-i);
}
i -= 1;
}
dec_num /= 100000;
len += decs;
i = len - 1;
while (i >= len - decs) {
buf[i] = '0' + u8(dec_num % 10);
dec_num /= 10;
i -= 1;
}
}
@memcpy(&out_buf[0], &buf[0], len);
len
}
fn min_isize(x: isize, y: isize) -> isize {
if (x < y) x else y
}