From 2c32157fd361f18a6bb63cc4cc37fd765e7a3840 Mon Sep 17 00:00:00 2001 From: John Dupuy Date: Thu, 14 May 2020 16:34:27 -0500 Subject: [PATCH] writeText for float rewritten to re-use Nim's call to sprintf --- faststreams/textio.nim | 161 +++++------------------------------------ 1 file changed, 17 insertions(+), 144 deletions(-) diff --git a/faststreams/textio.nim b/faststreams/textio.nim index 4b82d25..0fdf35b 100644 --- a/faststreams/textio.nim +++ b/faststreams/textio.nim @@ -1,7 +1,7 @@ import stew/ptrops, inputs, outputs, buffers, async_backend, multisync, - math + system/formatfloat template matchingIntType(T: type int64): type = uint64 template matchingIntType(T: type int32): type = uint32 @@ -92,149 +92,22 @@ proc writeText*(s: OutputStream, x: CompiledIntTypes) = else: writeText(s, MatchingUInt(x)) -when defined(aa): - proc writeText*(s: OutputStream, x: float64|float) = # this version mostly works, but does not account for rounding error - var whole = x.int - writeText(s, whole) - var frac = abs(x - whole.float) - if frac == 0.0: - return - write s, '.' - var limit: byte = 8 - while (frac != 0.0) and (limit > 0): - limit -= 1 - frac *= 10.0 - whole = frac.int - frac -= whole.float - write s, char(ord('0') + whole) - -when defined(bb): - proc writeText*(s: OutputStream, x: float64|float) = # this version just prints the integer part - writeText(s, x.int) - -when defined(cc): - proc writeText*(s: OutputStream, x: float64|float) = # this version uses ftoa of stdlibc - proc floatToStr(x: float): string {.magic: "FloatToStr", noSideEffect.} - writeText(s, floatToStr(x)) - -when defined(dd): - proc writeText*(s: OutputStream, x: float64|float) = # this version uses ftoa of stdlibc - write s, $x - -when defined(ee): - proc writeText*(s: OutputStream, x: float64|float) = # this version mostly works, but does not account for rounding error - if x == 0.0: - write s, "0.0" - return - if x < 0.0: - write s, '-' - var frac = x.abs - var place = log10(frac).int - if place < 0: - place = 0 - frac /= pow(10.0, place.float) - var whole: int - while (frac != 0.0) and (place > -100): - if place == -1: - write s, '.' - whole = frac.int - write s, char(ord('0') + whole) - frac = (frac - whole.float) * 10.0 - place -= 1 - -when defined(ff): - proc writeText*(s: OutputStream, x: float64|float) = # handles rounding - const PRECISION_LIMIT = 16 - const DECIMAL_POINT: uint8 = 255 - if x == 0.0: - write s, "0.0" - return - var digits: array[322, byte] - var index: int = 0 - if x < 0.0: - write s, '-' - var precision: int = 0 - var firstDigitFound = false - var decimalPointIndex = -1 - var frac = x.abs - var place = log10(frac).int - if place < 0: - place = 0 - frac /= pow(10.0, place.float) - var whole: uint8 - # - # run through the digits: - # - while (frac != 0.0) and (place > -317) and (precision < PRECISION_LIMIT): - if place == -1: - digits[index] = DECIMAL_POINT - decimalPointIndex = index - index += 1 - whole = frac.uint8 - digits[index] = whole - index += 1 - if firstDigitFound: - precision += 1 - else: - if whole != 0.uint8: - firstDigitFound = true - precision += 1 - frac = (frac - whole.float) * 10.0 - place -= 1 - # - # place any missing decimal points as a key marker - # - if decimalPointIndex == -1: - # if decimal point not expressed yet, then add it - index += place + 1 - digits[index] = DECIMAL_POINT - decimalPointIndex = index - index += 1 - index -= 1 - # - # remove trailing digits and adjust precision back if need be: - # - while (digits[index] == 0) and (index > 0): - precision -= 1 - index -= 1 - # - # if still at precision limit, then do rounding: - # - if precision >= PRECISION_LIMIT: - while index > 0: - if digits[index] == DECIMAL_POINT: - index -= 1 - continue - elif digits[index] >= 5: - digits[index - 1] += 1 - digits[index] = 0 - if digits[index - 1] < 10: - break - else: - digits[index] = 0 - break - index -= 1 - # - # remove trailing digits and adjust precision back if need be: - # - while (digits[index] == 0) and (index > 0): - precision -= 1 - index -= 1 - # - # express to the stream - # - if index < decimalPointIndex: - index = decimalPointIndex - for i in 0 .. index: - let d = digits[i] - if d == DECIMAL_POINT: - write s, '.' - elif d == 10: - write s, "10" - else: - write s, char(ord('0') + d) - if digits[index] == DECIMAL_POINT: - write s, '0' +when defined(c): + + proc writeText*(s: OutputStream, x: float64|float32|float) = + ## Write the floating point number to the output stream. It has less overhead + ## than `$` because it is directly written to the stream without + ## allocating a standard Nim 'string'. + ## + ## Because this procedure uses stdlib, it is only active when nim compiles + ## with the 'c' flag. Otherwise, float types fall back to the generic writeText + ## procedure. + var buffer: array[65, char] + discard writeFloatToBuffer(buffer, x) + for index in 0 ..< 65: + if buffer[index] == '\0': + break + write s, buffer[index] template writeText*(s: OutputStream, str: string) = write s, str