lancet icon indicating copy to clipboard operation
lancet copied to clipboard

formatter下的DecimalBytes非整数

Open imyuanandyou opened this issue 1 year ago • 6 comments

DecimalBytes : 返回十进制标准(以 1000 为基数)下的可读字节单位字符串。precision 参数指定小数点后的位数,默认为 4。

根据描述precision参数为控制小数点的位数,但某些情况下会变成科学计数法 例如

// 测试6000万这个数
formatter.DecimalBytes(61812496)
// 61.812MB

formatter.DecimalBytes(61812496, 2)
// 61.8MB

formatter.DecimalBytes(61812496, 1)
// 62MB

formatter.DecimalBytes(61812496, 0)
// 6e+01MB


//再测试4亿这个数
formatter.DecimalBytes(401000000)
//401MB

formatter.DecimalBytes(4010000, 0)
//4e+02MB

最后我的解决办法,不知道合不合理 缺点就是在401.00000MB整数的时候还是会带上小数位数,不知道如何优化了。但不会出现科学计数法的表示,可以接受

func DecimalBytes(size float64, precision ...int) string {
	p := 5
	if len(precision) > 0 {
		p = precision[0]
	}
	size, unit := calculateByteSize(size, 1000.0, decimalByteUnits)
	position := fmt.Sprintf("%%.%df", p)
	return fmt.Sprintf(position+"%s", size, unit)
}



// 测试6000万这个数
utils.DecimalBytes(61812496)
// 61.81250MB

utils.DecimalBytes(61812496, 2)
// 61.81MB

utils.DecimalBytes(61812496, 1)
// 61.8MB

utils.DecimalBytes(61812496, 0)
// 62MB


//再测试4亿这个数
utils.DecimalBytes(401000000)
//401.00000MB

utils.DecimalBytes(4010000, 0)
//4MB

imyuanandyou avatar Jul 27 '24 18:07 imyuanandyou

@imyuanandyou 可以对格式化之后的数字字符串进行处理,根据需要去除'.'和'0', 参考以下代码:

func DecimalBytes(size float64, precision ...int) string {
	pointPosition := 4
	if len(precision) > 0 {
		pointPosition = precision[0]
	}

	size, unit := calculateByteSize(size, 1000.0, decimalByteUnits)

	format := fmt.Sprintf("%%.%df", pointPosition)
	result := fmt.Sprintf(format, size)

	for i := len(result); i > 0; i-- {
		s := result[i-1]
		if s == '0' || s == '.' {
			result = result[:i-1]
		} else {
			break
		}
	}

	return result + unit
}

duke-git avatar Jul 29 '24 03:07 duke-git

thanks! 后续会考虑将这部分改动,放入正式版本吗?目的是DecimalBytes时不会出现科学计数法

imyuanandyou avatar Jul 30 '24 04:07 imyuanandyou

@imyuanandyou, 会放入正式版,这个已经在v2分支上修复,会在下个版本(v2.3.3)发布。

duke-git avatar Jul 30 '24 06:07 duke-git

后续测试后发现 (根据需要去除'.'和'0', 参考以下代码)这段代码会将整数0结尾也一同去掉

func main() {
	fmt.Println(DecimalBytes(40, 0))
// 理论得到40B,但结果是4B
}

var (
	decimalByteUnits = []string{"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
	binaryByteUnits  = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
)

func DecimalBytes(size float64, precision ...int) string {
	pointPosition := 4
	if len(precision) > 0 {
		pointPosition = precision[0]
	}

	size, unit := calculateByteSize(size, 1000.0, decimalByteUnits)

	format := fmt.Sprintf("%%.%df", pointPosition)
	result := fmt.Sprintf(format, size)

	for i := len(result); i > 0; i-- {
		s := result[i-1]
		if s == '0' || s == '.' {
			result = result[:i-1]
		} else {
			break
		}
	}

	return result + unit
}

func calculateByteSize(size float64, base float64, byteUnits []string) (float64, string) {
	i := 0
	unitsLimit := len(byteUnits) - 1
	for size >= base && i < unitsLimit {
		size = size / base
		i++
	}
	return size, byteUnits[i]
}

另外科学计数法同样会出现在formatter.BinaryBytes返回binary标准(以1024为基数) 这个方法

imyuanandyou avatar Aug 09 '24 11:08 imyuanandyou

然后贴一下我自己FloorToString的方法,max为想要保留的最大小数位,默认是两位。BinaryBytes、DecimalBytes感觉上是相同的需求

func FloorToString(x float64, max ...int) string {
	pointPosition := 2
	if len(max) > 0 {
		pointPosition = max[0]
	}
	result := mathutil.FloorToString(x, pointPosition)

	// 删除小数位结尾的0
	decimal := strings.TrimRight(strutil.After(result, "."), "0")
	if decimal == "" || pointPosition == 0 {
		// 没有小数位直接返回整数
		return strutil.Before(result, ".")
	}

	// 小数位大于想要设置的位数,按需要截断
	if len(decimal) > pointPosition {
		return strutil.Before(result, ".") + "." + decimal[:pointPosition]
	}

	// 小数位小于等于想要的位数,直接拼接返回
	return strutil.Before(result, ".") + "." + decimal
}
func main() {
	fmt.Println(utils.FloorToString(1000.0001, 2))
	// 输出1000

	fmt.Println(utils.FloorToString(1000.1001, 2))
	// 输出1000

	fmt.Println(utils.FloorToString(1000.1, 2))
	// 输出1000.1

	fmt.Println(utils.FloorToString(1000, 0))
	// 输出1000
}

imyuanandyou avatar Aug 09 '24 11:08 imyuanandyou

@imyuanandyou, 👍 这些解决方案,你可以提PR,merge到rc分支。

duke-git avatar Aug 09 '24 14:08 duke-git

v2.3.3版本已修复

duke-git avatar Oct 09 '24 09:10 duke-git