mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
`strutils.formatSize` returns correct strings from large values close to `int64.high`. Round down `bytes` when it is converted to float.
This commit is contained in:
@@ -74,6 +74,7 @@ import std/parseutils
|
||||
from std/math import pow, floor, log10
|
||||
from std/algorithm import fill, reverse
|
||||
import std/enumutils
|
||||
from std/bitops import fastLog2
|
||||
|
||||
from std/unicode import toLower, toUpper
|
||||
export toLower, toUpper
|
||||
@@ -2639,37 +2640,35 @@ func formatSize*(bytes: int64,
|
||||
## * `strformat module<strformat.html>`_ for string interpolation and formatting
|
||||
runnableExamples:
|
||||
doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
|
||||
doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
|
||||
doAssert formatSize((2.234*1024*1024).int) == "2.233MiB"
|
||||
doAssert formatSize(4096, includeSpace = true) == "4 KiB"
|
||||
doAssert formatSize(4096, prefix = bpColloquial, includeSpace = true) == "4 kB"
|
||||
doAssert formatSize(4096) == "4KiB"
|
||||
doAssert formatSize(5_378_934, prefix = bpColloquial, decimalSep = ',') == "5,13MB"
|
||||
doAssert formatSize(5_378_934, prefix = bpColloquial, decimalSep = ',') == "5,129MB"
|
||||
|
||||
const iecPrefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"]
|
||||
const collPrefixes = ["", "k", "M", "G", "T", "P", "E", "Z", "Y"]
|
||||
var
|
||||
xb: int64 = bytes
|
||||
fbytes: float
|
||||
lastXb: int64 = bytes
|
||||
matchedIndex = 0
|
||||
prefixes: array[9, string]
|
||||
# It doesn't needs Zi and larger units until we use int72 or larger ints.
|
||||
const iecPrefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"]
|
||||
const collPrefixes = ["", "k", "M", "G", "T", "P", "E"]
|
||||
|
||||
let lg2 = if bytes == 0:
|
||||
0
|
||||
else:
|
||||
when hasWorkingInt64:
|
||||
fastLog2(bytes)
|
||||
else:
|
||||
fastLog2(int32 bytes)
|
||||
let matchedIndex = lg2 div 10
|
||||
# Lower bits that are smaller than 0.001 when `bytes` is converted to a real number and added prefix, are discard.
|
||||
# Then it is converted to float with round down.
|
||||
let discardBits = (lg2 div 10 - 1) * 10
|
||||
|
||||
var prefixes: array[7, string]
|
||||
if prefix == bpColloquial:
|
||||
prefixes = collPrefixes
|
||||
else:
|
||||
prefixes = iecPrefixes
|
||||
|
||||
# Iterate through prefixes seeing if value will be greater than
|
||||
# 0 in each case
|
||||
for index in 1..<prefixes.len:
|
||||
lastXb = xb
|
||||
xb = bytes div (1'i64 shl (index*10))
|
||||
matchedIndex = index
|
||||
if xb == 0:
|
||||
xb = lastXb
|
||||
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
|
||||
let fbytes = if lg2 < 10: bytes.float elif lg2 < 20: bytes.float / 1024.0 else: (bytes shr discardBits).float / 1024.0
|
||||
result = formatFloat(fbytes, format = ffDecimal, precision = 3,
|
||||
decimalSep = decimalSep)
|
||||
result.trimZeros(decimalSep)
|
||||
|
||||
@@ -789,12 +789,71 @@ bar
|
||||
block: # formatSize
|
||||
disableVm:
|
||||
when hasWorkingInt64:
|
||||
doAssert formatSize(1024 * 1024 * 1024 * 2 - 1) == "1.999GiB"
|
||||
doAssert formatSize(1024 * 1024 * 1024 * 2) == "2GiB"
|
||||
doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB" # <=== bug #8231
|
||||
doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
|
||||
doAssert formatSize(int64.high) == "7.999EiB"
|
||||
doAssert formatSize(int64.high div 2 + 1) == "4EiB"
|
||||
doAssert formatSize(int64.high div 2) == "3.999EiB"
|
||||
doAssert formatSize(int64.high div 4 + 1) == "2EiB"
|
||||
doAssert formatSize(int64.high div 4) == "1.999EiB"
|
||||
doAssert formatSize(int64.high div 8 + 1) == "1EiB"
|
||||
doAssert formatSize(int64.high div 8) == "1023.999PiB"
|
||||
doAssert formatSize(int64.high div 16 + 1) == "512PiB"
|
||||
doAssert formatSize(int64.high div 16) == "511.999PiB"
|
||||
doAssert formatSize(0) == "0B"
|
||||
doAssert formatSize(0, includeSpace = true) == "0 B"
|
||||
doAssert formatSize(1) == "1B"
|
||||
doAssert formatSize(2) == "2B"
|
||||
doAssert formatSize(1022) == "1022B"
|
||||
doAssert formatSize(1023) == "1023B"
|
||||
doAssert formatSize(1024) == "1KiB"
|
||||
doAssert formatSize(1025) == "1.001KiB"
|
||||
doAssert formatSize(1026) == "1.002KiB"
|
||||
doAssert formatSize(1024 * 2 - 2) == "1.998KiB"
|
||||
doAssert formatSize(1024 * 2 - 1) == "1.999KiB"
|
||||
doAssert formatSize(1024 * 2) == "2KiB"
|
||||
doAssert formatSize(1024 * 2 + 1) == "2.001KiB"
|
||||
doAssert formatSize(1024 * 2 + 2) == "2.002KiB"
|
||||
doAssert formatSize(4096 - 1) == "3.999KiB"
|
||||
doAssert formatSize(4096) == "4KiB"
|
||||
doAssert formatSize(4096 + 1) == "4.001KiB"
|
||||
doAssert formatSize(1024 * 512 - 1) == "511.999KiB"
|
||||
doAssert formatSize(1024 * 512) == "512KiB"
|
||||
doAssert formatSize(1024 * 512 + 1) == "512.001KiB"
|
||||
doAssert formatSize(1024 * 1024 - 2) == "1023.998KiB"
|
||||
doAssert formatSize(1024 * 1024 - 1) == "1023.999KiB"
|
||||
doAssert formatSize(1024 * 1024) == "1MiB"
|
||||
doAssert formatSize(1024 * 1024 + 1) == "1MiB"
|
||||
doAssert formatSize(1024 * 1024 + 1023) == "1MiB"
|
||||
doAssert formatSize(1024 * 1024 + 1024) == "1.001MiB"
|
||||
doAssert formatSize(1024 * 1024 + 1024 * 2) == "1.002MiB"
|
||||
doAssert formatSize(1024 * 1024 * 2 - 1) == "1.999MiB"
|
||||
doAssert formatSize(1024 * 1024 * 2) == "2MiB"
|
||||
doAssert formatSize(1024 * 1024 * 2 + 1) == "2MiB"
|
||||
doAssert formatSize(1024 * 1024 * 2 + 1024) == "2.001MiB"
|
||||
doAssert formatSize(1024 * 1024 * 2 + 1024 * 2) == "2.002MiB"
|
||||
doAssert formatSize(1024 * 1024 * 4 - 1) == "3.999MiB"
|
||||
doAssert formatSize(1024 * 1024 * 4) == "4MiB"
|
||||
doAssert formatSize(1024 * (1024 * 4 + 1)) == "4.001MiB"
|
||||
doAssert formatSize(1024 * 1024 * 512 - 1025) == "511.998MiB"
|
||||
doAssert formatSize(1024 * 1024 * 512 - 1) == "511.999MiB"
|
||||
doAssert formatSize(1024 * 1024 * 512) == "512MiB"
|
||||
doAssert formatSize(1024 * 1024 * 512 + 1) == "512MiB"
|
||||
doAssert formatSize(1024 * 1024 * 512 + 1024) == "512.001MiB"
|
||||
doAssert formatSize(1024 * 1024 * 512 + 1024 * 2) == "512.002MiB"
|
||||
doAssert formatSize(1024 * 1024 * 1024 - 1) == "1023.999MiB"
|
||||
doAssert formatSize(1024 * 1024 * 1024) == "1GiB"
|
||||
doAssert formatSize(1024 * 1024 * 1024 + 1) == "1GiB"
|
||||
doAssert formatSize(1024 * 1024 * 1025) == "1.001GiB"
|
||||
doAssert formatSize(1024 * 1024 * 1026) == "1.002GiB"
|
||||
# != 2.234MiB as (2.234 * 1024 * 1024).int.float / (1024 * 1024) = 2.23399...
|
||||
# and formatSize round down the value
|
||||
doAssert formatSize((2.234*1024*1024).int) == "2.233MiB"
|
||||
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"
|
||||
# (5378934).float / (1024 * 1024) = 5.12975...
|
||||
doAssert formatSize(5_378_934, prefix = bpColloquial, decimalSep = ',') == "5,129MB"
|
||||
|
||||
block: # formatEng
|
||||
disableVm:
|
||||
|
||||
Reference in New Issue
Block a user