From 03a0dfbeca4b31d235097c66c9891d825cf73c15 Mon Sep 17 00:00:00 2001 From: scurest Date: Mon, 23 Oct 2017 15:40:49 -0500 Subject: [PATCH 1/3] Print better floats --- std/fmt/errol/index.zig | 12 ++++++------ std/fmt/index.zig | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index ac9f6b0c6..8cf13bca2 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -38,7 +38,7 @@ fn errol3u(val: f64, buffer: []u8) -> FloatDecimal { return errolFixed(val, buffer); } - + // normalize the midpoint const e = math.frexp(val).exponent; @@ -138,7 +138,7 @@ fn tableLowerBound(k: u64) -> usize { while (j < enum3.len) { if (enum3[j] < k) { - j = 2 * k + 2; + j = 2 * j + 2; } else { i = j; j = 2 * j + 1; @@ -217,7 +217,7 @@ fn hpMul10(hp: &HP) { hp.val *= 10.0; hp.off *= 10.0; - + var off = hp.val; off -= val * 8.0; off -= val * 2.0; @@ -241,7 +241,7 @@ fn errolInt(val: f64, buffer: []u8) -> FloatDecimal { var low: u128 = mid - fpeint((fpnext(val) - val) / 2.0); var high: u128 = mid + fpeint((val - fpprev(val)) / 2.0); - if (@bitCast(u64, val) & 0x1 != 0) { + if (@bitCast(u64, val) & 0x1 != 0) { high -= 1; } else { low -= 1; @@ -347,11 +347,11 @@ fn errolFixed(val: f64, buffer: []u8) -> FloatDecimal { } fn fpnext(val: f64) -> f64 { - return @bitCast(f64, @bitCast(u64, val) + 1); + return @bitCast(f64, @bitCast(u64, val) +% 1); } fn fpprev(val: f64) -> f64 { - return @bitCast(f64, @bitCast(u64, val) - 1); + return @bitCast(f64, @bitCast(u64, val) -% 1); } pub const c_digits_lut = []u8 { diff --git a/std/fmt/index.zig b/std/fmt/index.zig index b378afa1b..85688b361 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -244,30 +244,46 @@ pub fn formatBuf(buf: []const u8, width: usize, } pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool { - var buffer: [20]u8 = undefined; - const float_decimal = errol3(f64(value), buffer[0..]); - if (float_decimal.exp != 0) { - if (!output(context, float_decimal.digits[0..1])) - return false; - } else { - if (!output(context, "0")) - return false; + var x = f64(value); + + // Errol doesn't handle these special cases. + if (math.isNan(x)) { + return output(context, "NaN"); } + if (math.isPositiveInf(x)) { + return output(context, "Infinity"); + } + if (math.isNegativeInf(x)) { + return output(context, "-Infinity"); + } + if (x == 0.0) { + return output(context, "0.0"); + } + if (x < 0.0) { + if (!output(context, "-")) + return false; + x = -x; + } + + var buffer: [32]u8 = undefined; + const float_decimal = errol3(x, buffer[0..]); + if (!output(context, float_decimal.digits[0..1])) + return false; if (!output(context, ".")) return false; if (float_decimal.digits.len > 1) { - const start = if (float_decimal.exp == 0) usize(0) else usize(1); - if (!output(context, float_decimal.digits[start .. math.min(usize(7), float_decimal.digits.len)])) + const num_digits = if (@typeOf(value) == f32) { usize(8) } else { usize(17) }; + if (!output(context, float_decimal.digits[1 .. math.min(num_digits, float_decimal.digits.len)])) return false; } else { if (!output(context, "0")) return false; } - if (float_decimal.exp != 1 and float_decimal.exp != 0) { + if (float_decimal.exp != 1) { if (!output(context, "e")) return false; - if (!formatInt(float_decimal.exp, 10, false, 0, context, output)) + if (!formatInt(float_decimal.exp - 1, 10, false, 0, context, output)) return false; } return true; From 262b7428cfad88de9d328773cc4bae6151b8a030 Mon Sep 17 00:00:00 2001 From: scurest Date: Tue, 24 Oct 2017 14:18:50 -0500 Subject: [PATCH 2/3] More corrections to float printing Testing suggests all f32s are now printed accurately. --- std/fmt/errol/index.zig | 10 +++------- std/fmt/index.zig | 25 +++++++++++++------------ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index 8cf13bca2..0a1ea7dee 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -32,7 +32,7 @@ pub fn errol3(value: f64, buffer: []u8) -> FloatDecimal { fn errol3u(val: f64, buffer: []u8) -> FloatDecimal { // check if in integer or fixed range - if (val >= 9.007199254740992e15 and val < 3.40282366920938e+38) { + if (val > 9.007199254740992e15 and val < 3.40282366920938e+38) { return errolInt(val, buffer); } else if (val >= 16.0 and val < 9.007199254740992e15) { return errolFixed(val, buffer); @@ -235,7 +235,7 @@ fn hpMul10(hp: &HP) { fn errolInt(val: f64, buffer: []u8) -> FloatDecimal { const pow19 = u128(1e19); - assert((val >= 9.007199254740992e15) and val < (3.40282366920938e38)); + assert((val > 9.007199254740992e15) and val < (3.40282366920938e38)); var mid = u128(val); var low: u128 = mid - fpeint((fpnext(val) - val) / 2.0); @@ -510,10 +510,6 @@ fn u64toa(value_param: u64, buffer: []u8) -> usize { buf_index += 1; buffer[buf_index] = c_digits_lut[d8]; buf_index += 1; - buffer[buf_index] = c_digits_lut[d8]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d8]; - buf_index += 1; buffer[buf_index] = c_digits_lut[d8 + 1]; buf_index += 1; } else { @@ -613,7 +609,7 @@ fn fpeint(from: f64) -> u128 { const bits = @bitCast(u64, from); assert((bits & ((1 << 52) - 1)) == 0); - return u64(1) << u6(((bits >> 52) - 1023)); + return u128(1) << @truncate(u7, (bits >> 52) -% 1023); } diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 85688b361..0fece54b4 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -250,20 +250,17 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons if (math.isNan(x)) { return output(context, "NaN"); } - if (math.isPositiveInf(x)) { - return output(context, "Infinity"); - } - if (math.isNegativeInf(x)) { - return output(context, "-Infinity"); - } - if (x == 0.0) { - return output(context, "0.0"); - } - if (x < 0.0) { + if (math.signbit(x)) { if (!output(context, "-")) return false; x = -x; } + if (math.isPositiveInf(x)) { + return output(context, "Infinity"); + } + if (x == 0.0) { + return output(context, "0.0"); + } var buffer: [32]u8 = undefined; const float_decimal = errol3(x, buffer[0..]); @@ -272,8 +269,12 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons if (!output(context, ".")) return false; if (float_decimal.digits.len > 1) { - const num_digits = if (@typeOf(value) == f32) { usize(8) } else { usize(17) }; - if (!output(context, float_decimal.digits[1 .. math.min(num_digits, float_decimal.digits.len)])) + const num_digits = if (@typeOf(value) == f32) { + math.min(usize(9), float_decimal.digits.len) + } else { + float_decimal.digits.len + }; + if (!output(context, float_decimal.digits[1 .. num_digits])) return false; } else { if (!output(context, "0")) From 73fe5f63c6acc1b2e6fec51da545178ffd12180e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Oct 2017 21:45:00 -0400 Subject: [PATCH 3/3] add some sanity tests for float printing --- std/fmt/index.zig | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 0fece54b4..56b9a2e96 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -531,6 +531,38 @@ test "fmt.format" { const result = bufPrint(buf1[0..], "u3: {}\n", value); assert(mem.eql(u8, result, "u3: 5\n")); } + + // TODO get these tests passing in release modes + // https://github.com/zig-lang/zig/issues/564 + if (builtin.mode == builtin.Mode.Debug) { + { + var buf1: [32]u8 = undefined; + const value: f32 = 12.34; + const result = bufPrint(buf1[0..], "f32: {}\n", value); + assert(mem.eql(u8, result, "f32: 1.23400001e1\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = -12.34e10; + const result = bufPrint(buf1[0..], "f64: {}\n", value); + assert(mem.eql(u8, result, "f64: -1.234e11\n")); + } + { + var buf1: [32]u8 = undefined; + const result = bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); + assert(mem.eql(u8, result, "f64: NaN\n")); + } + { + var buf1: [32]u8 = undefined; + const result = bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); + assert(mem.eql(u8, result, "f64: Infinity\n")); + } + { + var buf1: [32]u8 = undefined; + const result = bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); + assert(mem.eql(u8, result, "f64: -Infinity\n")); + } + } } pub fn trim(buf: []const u8) -> []const u8 {