菜鸟IT的博客 >> Python
【四舍五入的精度解决了!但是会影响计算性能和效率】二级制运算的精度丢失,精度误差,导致小数点后四舍五不入,decimal.Context(prec=5,rounding=decimal.ROUND_HALF_UP).create_decimal(str(x)) | 无法求浮点数小数保留小数点后面几位的问题 | prec方法参数的作用原理
import decimal # 用于求数字精度有关的库
class ShuZi_4she5ru_prec(object):
try:
# 如果是直接str(0.00000000000006)会变成科学计数的格式,
# 而且浮点数默认是小数点后面5位。
# 所以这里写1个函数就是为了让数字能完整的转化成字符串。
def float_to_str(self,f):
ctx = decimal.Context()
d1 = ctx.create_decimal(repr(f))
return format(d1, 'f')
# 注意 ↑ 上面的结果得到的是字符串
# —————————————— 分割线 ———————————————— #
# 原先参考的代码,这个是用精度来数字的。四舍五入没问题了。但是需要对这个数字进行判断。
def Get_ShuZi_by_prec_JingDu(self,ShuZi, Int_prec):
return decimal.Context(prec=Int_prec, rounding=decimal.ROUND_HALF_UP).create_decimal(str(ShuZi))
# —————————————— 分割线 ———————————————— #
# 重新改进代码↓
# 首先理解prec这个参数干嘛的。
# prec这个参数是“非0的整数位包含起来的长度,而且是从最左边整数部分第1个整数开始[包含]”
# 举几个例子就明白了
# 比如 x = 0.05055 , 精度prec=3,那么输出 y 就是:0.0506
# 比如 x = 1.05055 ,精度prec=3,那么输出 y 就是:1.05
# 比如 x = 10.05055 , 精度prec=3,那么输出 y 就是:10.1
# 比如 x = 100.05055 , 精度prec=3,那么输出 y 就是:100
# 比如 x = 1015.05055,精度prec=3,那么输出 y 就是:1.02E+3 (结果变成科学计数的样子了,原因是prec的值,已经小于整数位的长度了)
# 比如 x = 10.000,精度prec=3.那么这个数字只有最左边第1个是非0整数,后面全部是0,经过我测试,不管我设置的精度多大,返回都是10.0只保留小数点后面1位0
# 比如 x = 10.0005,那么,这个数可以设置最大的精度是6,我只设置精度prec=5,输出结果就是10.001
# 比如 x = 10.0005,最大可设置的精度是6,我现在只设置4,只设置3,输出结果都是10.0
# 比如 x = 10.0001,最大可设置精度是6,我现在只设置5,我们试试看,输出结果是:10.000
# 比如 x = 10000.003,最大可设置精度是8,我现在只设置4,我们试试看,输出结果是:1.000E+4 ,所以最小精度不能小于整数位的位数。
# 比如 x = 10000.003,最大可设置精度是8,我现在只设置5,我们试试看,输出结果是:10000 ,所以精度刚好等于整数位的位数,输出整数。
# 就是说在你设置的精度范围内,(精度+1)位数字还是0的话,那就返回(整数.0)这样的数据了。
# 看到上面几个例子,应该就明白 prec 的作用和原理,以及缺点了。
def Get_ShuZi_4she5ru_all_Type(self,ShuZi,MaxN_after_Point):
# 这个函数思路是:整数直接返回整数。
# 如果是小数,MaxN_after_Point代表“小数点”后最多几位,不是固定几位。如果返回结果的尾部超出3位小数的0,要去掉。
if(type(ShuZi)==int):
return ShuZi
elif(type(ShuZi)==float):
# 如果是小数,先用float函数执行下,这样可以把小数点后面最后1位后面多余的0去掉
# 举3个例子:
# 例子1:float(10.00000)=10.0
# 例子2:float(10.10000)=10.1
# 例子3:float(10.11000)=10.11
# 总结:float方法执行一下的效果就是把当前小数变成1个至小数点后面1位的小数,如果是整数,后面跟1个0
# ★这里先强制转化下!但是不要直接用 float 转化,如果碰上小数点后超过5位的小数浮点数,转化结果就变成科学计数了。
ShuZi_Str=ShuZi_4she5ru_prec().float_to_str(ShuZi)
# 把这个数字强制用小数点分割下。
if(ShuZi_Str.split(".")[1]=="0"):
# 如果分割后,小数点后面是0,那就直接返回整数就行了。
return ShuZi
else:
# 现在的情况就是小数点后面最尾部不再有“纯0全0”的情况了。
# ——————————————————
# 分两种情况,小数点后面的数字位数是否超过MaxN_after_Point
# 先声明1个变量:N_after_Point 为当前小数的小数点后面的位数。
N_after_Point = len(ShuZi_Str.split(".")[1])
# ——————————————————
if(MaxN_after_Point>=N_after_Point):
# 这种情况下就直接返回float后的数字。
return ShuZi
elif(MaxN_after_Point<N_after_Point):
# 接下来的情况就只是↓
# ★【函数参数:最多精确到小数点后几位】小于【当前小数点后位数】。
# 只需要先确定小数精确到小数点后几位。然后把数字从最左边第1个数字,到 精确到小数点的位数这1位。
# 注意,把浮点数当1个字符串,从左侧第1个字符串开始找,
# 查找起点:最左侧第1个数,
# 查找重点:精确到小数点后第几位
# ————————————————————————
# 这个时候就要开始判断“prec”这个精确度参数了。
# ————————————————————————
# 为了方便分析“精度:prec”,把这个小数拆分成2个列表,以小数点为界。
ShuZi_List_before_Point = list(ShuZi_Str.split(".")[0])
ShuZi_List_after_Point = list(ShuZi_Str.split(".")[1])
# 这里再次重申一下prec这个参数的计算原理
# 从左侧第1个非0整数为起点,到下1个非0整数为结束点。
# “起点”和“结束点”之间是否存在0,不管。
# 包含“起点”和“结束点”在内,一共几个数位。
# 这个数位共计多少位就是【prec】。
ShuZi_List_after_Point_New=[]
for x in range(0,MaxN_after_Point):
ShuZi_List_after_Point_New.append(ShuZi_List_after_Point[x])
ShuZi_List_All = ShuZi_List_before_Point + ShuZi_List_after_Point_New
# ——————————————
# 这里定义1个变量,First_No_0_index,初始化等于0
First_No_0_index=0
# for 循环开始找。
for i in range(len(ShuZi_List_All)):
if(int(ShuZi_List_All[i])>0):
First_No_0_index=ShuZi_List_All.index(ShuZi_List_All[i])
break
print("测试找到第1个非0整数的索引:",First_No_0_index)
# ————————————————————
# 动态的分析出这个精度值。
precX = len(ShuZi_List_All) - First_No_0_index
# ————————————————————
# 比如:1.0005,那么First_No_0_index就是0,那么这个总位数是5,那么5减去0就等于5
# prec就是起点(包含) 到 结束点(包含)的总位数。
# ————————————————————
# 再比如:0.0006,那么First_No_0_index就是4,总位数是5,那么5减去4就等于1
# prec就等于1
# ————————————————————
return decimal.Context(prec=precX, rounding=decimal.ROUND_HALF_UP).create_decimal(ShuZi_Str)
# 完美求精度的函数完工。
else:
# 其他情况,直接返回“空”
return None
# —————————————— 分割线 ———————————————— #
except BaseException as e:
print(str(e))
# ■■■■■■■■■■■■■■■■■■ 本地测试分割线 ■■■■■■■■■■■■■■■■■■ #
if __name__=="__main__":
# print("原始的参考代码,精度prec需要自己手动调整,第2个参数就是精度:", ShuZi_4she5ru_prec().Get_ShuZi_by_prec_JingDu(1.05055,5))
# print("新改进的代码,故意写1个整数,函数无视后面的【最多小数点后几位参数】:",ShuZi_4she5ru_prec().Get_ShuZi_4she5ru_all_Type(100,5))
# print("当第2个参数【最大小数点后的位数】大于【当前小数的真实小数点后位数】:",ShuZi_4she5ru_prec().Get_ShuZi_4she5ru_all_Type(10.001,5))
# print("★当第2个参数【最大小数点后的位数】等于【当前小数(10.001)的真实小数点后位数】:", ShuZi_4she5ru_prec().Get_ShuZi_4she5ru_all_Type(10.001, 3))
# print("★当第2个参数【最大小数点后的位数】等于【当前小数(10.000)的真实小数点后位数】:", ShuZi_4she5ru_prec().Get_ShuZi_4she5ru_all_Type(10.000, 3))
print("#"*50)
print("★当第2个参数【最大小数点后的位数】小于【当前小数的真实小数点后位数】:", ShuZi_4she5ru_prec().Get_ShuZi_4she5ru_all_Type(10.60555,3))
菜鸟IT博客[2022.03.25-02:20] 访问:304