Reimplementation of formatSize to return a more meaningful value (fixes #4198).
This commit is contained in:
parent
24a56a2cb6
commit
d855ad7045
2 changed files with 95 additions and 23 deletions
|
|
@ -1392,7 +1392,7 @@ type
|
||||||
|
|
||||||
proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
||||||
precision: range[0..32] = 16;
|
precision: range[0..32] = 16;
|
||||||
decimalSep = '.'): string {.
|
decimalSep = '.', trim = false): string {.
|
||||||
noSideEffect, rtl, extern: "nsu$1".} =
|
noSideEffect, rtl, extern: "nsu$1".} =
|
||||||
## Converts a floating point value `f` to a string.
|
## Converts a floating point value `f` to a string.
|
||||||
##
|
##
|
||||||
|
|
@ -1404,6 +1404,8 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
||||||
## after the decimal point for Nim's ``biggestFloat`` type.
|
## after the decimal point for Nim's ``biggestFloat`` type.
|
||||||
##
|
##
|
||||||
## If ``precision == 0``, it tries to format it nicely.
|
## If ``precision == 0``, it tries to format it nicely.
|
||||||
|
##
|
||||||
|
## If ``trim == true``, trailing zeros will be removed.
|
||||||
when defined(js):
|
when defined(js):
|
||||||
var res: cstring
|
var res: cstring
|
||||||
case format
|
case format
|
||||||
|
|
@ -1423,6 +1425,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
||||||
var
|
var
|
||||||
frmtstr {.noinit.}: array[0..5, char]
|
frmtstr {.noinit.}: array[0..5, char]
|
||||||
buf {.noinit.}: array[0..2500, char]
|
buf {.noinit.}: array[0..2500, char]
|
||||||
|
splResult: seq[string]
|
||||||
L: cint
|
L: cint
|
||||||
frmtstr[0] = '%'
|
frmtstr[0] = '%'
|
||||||
if precision > 0:
|
if precision > 0:
|
||||||
|
|
@ -1443,8 +1446,21 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
||||||
if buf[i] in {'.', ','}: result[i] = decimalsep
|
if buf[i] in {'.', ','}: result[i] = decimalsep
|
||||||
else: result[i] = buf[i]
|
else: result[i] = buf[i]
|
||||||
|
|
||||||
|
# Trim trailing zeros if required (used by formatSize)
|
||||||
|
if trim and result.contains(decimalSep):
|
||||||
|
if result.contains('e'):
|
||||||
|
splResult = result.split('e')
|
||||||
|
result = splResult[0]
|
||||||
|
while result[result.high] == '0':
|
||||||
|
result.setLen(result.len-1)
|
||||||
|
if result[result.high] == decimalSep:
|
||||||
|
result.setLen(result.len-1)
|
||||||
|
if splResult.len > 0:
|
||||||
|
result &= "e" & splResult[1]
|
||||||
|
|
||||||
proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
||||||
precision: range[0..32] = 16; decimalSep = '.'): string {.
|
precision: range[0..32] = 16; decimalSep = '.',
|
||||||
|
trim = false): string {.
|
||||||
noSideEffect, rtl, extern: "nsu$1".} =
|
noSideEffect, rtl, extern: "nsu$1".} =
|
||||||
## Converts a floating point value `f` to a string.
|
## Converts a floating point value `f` to a string.
|
||||||
##
|
##
|
||||||
|
|
@ -1454,30 +1470,69 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
||||||
## of significant digits to be printed.
|
## of significant digits to be printed.
|
||||||
## `precision`'s default value is the maximum number of meaningful digits
|
## `precision`'s default value is the maximum number of meaningful digits
|
||||||
## after the decimal point for Nim's ``float`` type.
|
## after the decimal point for Nim's ``float`` type.
|
||||||
result = formatBiggestFloat(f, format, precision, decimalSep)
|
## If `trim` is set to true, trailing zeros will be removed.
|
||||||
|
result = formatBiggestFloat(f, format, precision, decimalSep, trim)
|
||||||
|
|
||||||
proc formatSize*(bytes: BiggestInt, decimalSep = '.'): string =
|
type
|
||||||
## Rounds and formats `bytes`. Examples:
|
BinaryPrefixMode* = enum ## the different names for binary prefixes
|
||||||
|
bpIEC, # use the IEC/ISO standard prefixes such as kibi
|
||||||
|
bpColloquial # use the colloquial kilo, mega etc
|
||||||
|
|
||||||
|
proc formatSize*(bytes: int64,
|
||||||
|
decimalSep = '.',
|
||||||
|
prefix = bpIEC,
|
||||||
|
includeSpace = false): string =
|
||||||
|
## Rounds and formats `bytes`.
|
||||||
|
##
|
||||||
|
## By default, uses the IEC/ISO standard binary prefixes, so 1024 will be
|
||||||
|
## formatted as 1KiB. Set prefix to `bpColloquial` to use the colloquial
|
||||||
|
## names from the SI standard (e.g. k for 1000 being reused as 1024).
|
||||||
|
##
|
||||||
|
## `includeSpace` can be set to true to include the (SI preferred) space
|
||||||
|
## between the number and the unit (e.g. 1 KiB).
|
||||||
|
##
|
||||||
|
## Examples:
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## formatSize(1'i64 shl 31 + 300'i64) == "2.204GB"
|
## formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
|
||||||
## formatSize(4096) == "4KB"
|
## formatSize((2.234*1024*1024).int) == "2.234MiB"
|
||||||
|
## formatSize(4096, includeSpace=true) == "4 KiB"
|
||||||
|
## formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
|
||||||
|
## formatSize(4096) == "4KiB"
|
||||||
|
## formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
|
||||||
##
|
##
|
||||||
template frmt(a, b, c: expr): expr =
|
const iecPrefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"]
|
||||||
let bs = $b
|
const collPrefixes = ["", "k", "M", "G", "T", "P", "E", "Z", "Y"]
|
||||||
insertSep($a) & decimalSep & bs.substr(0, 2) & c
|
var
|
||||||
let gigabytes = bytes shr 30
|
xb: int64 = bytes
|
||||||
let megabytes = bytes shr 20
|
fbytes: float
|
||||||
let kilobytes = bytes shr 10
|
last_xb: int64 = bytes
|
||||||
if gigabytes != 0:
|
matchedIndex: int
|
||||||
result = frmt(gigabytes, megabytes, "GB")
|
prefixes: array[9, string]
|
||||||
elif megabytes != 0:
|
if prefix == bpColloquial:
|
||||||
result = frmt(megabytes, kilobytes, "MB")
|
prefixes = collPrefixes
|
||||||
elif kilobytes != 0:
|
|
||||||
result = frmt(kilobytes, bytes, "KB")
|
|
||||||
else:
|
else:
|
||||||
result = insertSep($bytes) & "B"
|
prefixes = iecPrefixes
|
||||||
|
|
||||||
|
# Iterate through prefixes seeing if value will be greater than
|
||||||
|
# 0 in each case
|
||||||
|
for index in 1..<prefixes.len:
|
||||||
|
last_xb = xb
|
||||||
|
xb = bytes div (1'i64 shl (index*10))
|
||||||
|
matchedIndex = index
|
||||||
|
if xb == 0:
|
||||||
|
xb = last_xb
|
||||||
|
matchedIndex = index - 1
|
||||||
|
break
|
||||||
|
# xb has the integer number for the latest value; index should be correct
|
||||||
|
fbytes = bytes.float / (1'i64 shl (matchedIndex*10)).float
|
||||||
|
result = formatFloat(fbytes, format=ffDecimal, precision=3,
|
||||||
|
decimalSep=decimalSep, trim=true)
|
||||||
|
if includeSpace:
|
||||||
|
result &= " "
|
||||||
|
result &= prefixes[matchedIndex]
|
||||||
|
result &= "B"
|
||||||
|
|
||||||
proc findNormalized(x: string, inArray: openArray[string]): int =
|
proc findNormalized(x: string, inArray: openArray[string]): int =
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -1673,9 +1728,19 @@ when isMainModule:
|
||||||
["1,0e-11", "1,0e-011"]
|
["1,0e-11", "1,0e-011"]
|
||||||
|
|
||||||
doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
|
doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
|
||||||
when not defined(testing):
|
|
||||||
echo formatSize(1'i64 shl 31 + 300'i64) # == "4,GB"
|
block: # formatFloat trim tests
|
||||||
echo formatSize(1'i64 shl 31)
|
doAssert formatFloat(2.33, trim=true) == "2.33"
|
||||||
|
doAssert formatFloat(2.34e50, ffScientific, precision=8, trim=true) in
|
||||||
|
["2.34e+50", "2.34e+050"]
|
||||||
|
|
||||||
|
block: # formatSize tests
|
||||||
|
doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
|
||||||
|
doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
|
||||||
|
doAssert formatSize(4096) == "4KiB"
|
||||||
|
doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
|
||||||
|
doAssert formatSize(4096, includeSpace=true) == "4 KiB"
|
||||||
|
doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
|
||||||
|
|
||||||
doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
|
doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
|
||||||
"The cat eats fish."
|
"The cat eats fish."
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,11 @@ Changes affecting backwards compatibility
|
||||||
than changing the operation for even and odd numbers.
|
than changing the operation for even and odd numbers.
|
||||||
- The ``round`` function now accepts a ``places`` argument to round to a
|
- The ``round`` function now accepts a ``places`` argument to round to a
|
||||||
given number of places (e.g. round 4.35 to 4.4 if ``places`` is 1).
|
given number of places (e.g. round 4.35 to 4.4 if ``places`` is 1).
|
||||||
|
- In ``strutils.nim``, ``formatSize`` now returns a number representing the
|
||||||
|
size in conventional decimal format (e.g. 2.234GB meaning 2.234 GB rather
|
||||||
|
than meaning 2.285 GB as in the previous implementation). By default it
|
||||||
|
also uses IEC prefixes (KiB, MiB) etc and optionally uses colloquial names
|
||||||
|
(kB, MB etc) and the (SI-preferred) space.
|
||||||
|
|
||||||
|
|
||||||
Library Additions
|
Library Additions
|
||||||
|
|
@ -63,6 +68,8 @@ Library Additions
|
||||||
versions are for ``char`` and ``set[char]``.
|
versions are for ``char`` and ``set[char]``.
|
||||||
- Added ``splitDecimal`` to ``math.nim`` to split a floating point value
|
- Added ``splitDecimal`` to ``math.nim`` to split a floating point value
|
||||||
into an integer part and a floating part (in the range -1<x<1).
|
into an integer part and a floating part (in the range -1<x<1).
|
||||||
|
- Added ``trim`` argument to ``formatFloat`` in ``strutils.nim`` to trim
|
||||||
|
trailing zeros.
|
||||||
|
|
||||||
|
|
||||||
Compiler Additions
|
Compiler Additions
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue