数值修约程序(包括运算过程有效数字保留,Python3实现)

2022.09更新:考虑到需要用到这个程序的朋友很可能没有计算机基础(这也是我只放在csdn而没有放github的原因),我稍微加了一点注释,并且稍微重构了一下以提高可读性。

分割线后是2020原文


今年疫情期间应物理系的朋友要求做了一个做所谓数值和运算修约的程序

具体包括数值修约(说得简单些就是舍入,参考GB/T 8170-1987)和运算过程的有效数字的保留(就是加减乘除过程中对数值保留位数有要求。具体看我朋友给我提供的这份文件吧:https://shimo.im/docs/PH3hvV6CygW3wK36/)

不说废话了,下面直接贴完整代码吧,Python 3.7和3.8测试过是可以直接跑的。代码使用Python语言实现,所以需要Python解释器。限于篇幅,我在这里就不说怎么安装Python了。

  • 如果你也在找数值运算修约程序,为什么不试试呢?我试过,这个东西手算实在是太费脑筋了,还特别容易错。

  • 如果你看不懂 class或者self是什么,那就直接划到最底下看一下示范的用法,然后copy, paste and run吧。

  • 如果本程序使用的修约规则与你所需要的不同,你可以进行更改,毕竟很多基本的逻辑里面都已经实现好了。

等我有时间再考虑要不要把实现思路给写出来吧。

# %%
import math
from warnings import warn
from decimal import *
from typing import Union
# Ver 0.5, Copyleft 2020.6 - 2022.6 Peng Lingbo
# The documentation is written in CHINESE, since the program is based on GB/T 8170-1987
# if you can read Chinese but the following docs doesn't make sense, try to reopen file with UTF-8 encoding.
# v0.3 增加debug功能,可以用于查看追踪过程
# v0.4 支持用任何类型的数字来进行初始化,然而浮点类型本身存在精度问题,所以会收到一个警告要求你使用str()或者Decimal()
#      此外,现在已经支持运算中加入非Number类对象(内置数字),不过出于上面的原因,还是
#      建议用str类型--显式的str()转换或者是直接用'单引号'括起来--或者用Decimal类型.
# v0.5 优化程序逻辑和debug的呈现方式。初始化时可以直接指定精度了。# 由于使用者所在单位采取的运算修约标准可能与本程序所使用的有轻微不同,建议使用者先用几个测例打开debug=True检查各类运算是否符合期望
# 然后对于不符合的部分做自己的修改再使用。# 使用本程序,即代表你已经同意程序作者对由于使用本程序所带来的一切可能后果不负任何责任。class Number:"""帮你自动完成数值运算修约!"""def __init__(self, value:Union[Decimal, str, int, float], precision:int = None, debug:bool = False):"""初始化参数 value: 一个Decimal对象或一个数字的字符串形式,比如:\na = Number('3.51E2')\nb = Number('-8.000') <-注意这里的引号\nx = Decimal('3.14159')\nc = Number(x)\n注意:从程序的角度,直接使用浮点型小数也没问题,但是可能数值会和输入的不一样\n参数 precision: 整数型,指定精度到 10 ^ precision 位。默认为无,会根据value自动计算\n参数 debug: 布尔型,指定是否查看追踪过程。默认值 False,即不查看\n"""if isinstance(value, Number):for attr in dir(value):if attr[:2] != '__':setattr(self, attr, getattr(value, attr))if precision is not None and precision != self.effective_digits:self.round_inplace(precision)returnif isinstance(value, float):warn(f"Floating type can cause numerical problems. Use '{value}' or str({value}) to initialize to suppress this warning.")self.value = Decimal(value)self.effective_digits = Number._eff(self.value.__str__())self.debug = debugself.debugger = lambda x: print('[Debug]', x) if debug else lambda x: xif self.value != 0:# 这个数字的最高位的log10值,也就是它最高位的幂数self.highest_digit = math.floor(math.log10(abs(self.value)))# 这个数字的最低位的log10值,也就是它最低位的幂数self.lowest_digit = self.highest_digit - self.effective_digits + 1else:# 目前对数字0,默认有效位数为1,最高最低位幂指数为0self.highest_digit = 0self.lowest_digit = 0if precision is not None and precision != self.effective_digits:self.round_inplace(precision)else:self.debugger(f"{self.value} (Effectives={self.effective_digits}, Highest={self.highest_digit}, Lowest={self.lowest_digit})")def round_inplace(self, precision:int = 1):"""原地舍入(改变原变量)。\n参数:precision(需要保留的有效数字数量)\n"""self.lowest_digit = self.highest_digit - precision + 1old_val = self.valueself.value = self.value.quantize(Decimal('1e'+str(self.lowest_digit)), ROUND_HALF_EVEN)self.effective_digits = precisionself.debugger(f"{old_val} --> {self.value} (rounding to 1e{self.lowest_digit}, effectives={precision})")def round(self, precision:int = 1):"""舍入。\n参数:precision(需要保留的有效数字数量)\n返回:舍入之后的新Number对象\n"""return Number(self.value, precision, self.debug)@staticmethoddef _eff(num):"""接受一个数字并返回其有效数字数量\n参数:数字num(字符串形式)\n返回:num的有效数字数量"""assert isinstance(num, str)digits = 0is_leading_zero = Truefor d in num:if d == 'E' or d == 'e':breakdigit = ord(d) - ord('0')if 0 <= digit <= 9:if digit > 0 or (digit == 0 and not is_leading_zero):digits += 1  # 每遇到有效数字: digit++is_leading_zero = False  # 遇到第一个有效数字后所有的0都不是leading zero了,都是有效数字return max(digits, 1)def __add__(self, rhs):"""加法运算符"""if not isinstance(rhs, Number):rhs = Number(rhs)if self.value == 0:return rhsif rhs.value == 0:return selfif rhs.debug:self.debugger = rhs.debuggerresult = self.value + rhs.valuelow_digit = max(self.lowest_digit, rhs.lowest_digit)self.debugger(f"{self} + {rhs} = {result} --> (will round to 1e{low_digit})")res = result.quantize(Decimal('1e'+str(low_digit)), ROUND_HALF_EVEN)res = Number(res, debug = self.debug or rhs.debug)return resdef __sub__(self, rhs):"""减法运算符"""if not isinstance(rhs, Number):rhs = Number(rhs)if self.value == 0:rhs.value = -rhs.valuereturn rhsif rhs.value == 0:return selfif rhs.debug:self.debugger = rhs.debuggerresult = self.value - rhs.valuelow_digit = max(self.lowest_digit, rhs.lowest_digit)self.debugger(f"{self} - {rhs} = {result} --> (will round to 1e{low_digit})")res = result.quantize(Decimal('1e'+str(low_digit)), ROUND_HALF_EVEN)res = Number(res, debug = self.debug or rhs.debug)return resdef __mul__(self, rhs):"""乘法运算符"""if not isinstance(rhs, Number):rhs = Number(rhs)if rhs.debug:self.debugger = rhs.debuggerresult = self.value * rhs.valueleft_high = self.value // Decimal(10**self.highest_digit)right_high = rhs.value // Decimal(10**rhs.highest_digit)carry = (left_high.log10() + right_high.log10()).__floor__()precision = min(self.effective_digits, rhs.effective_digits) + carryself.debugger(f"{self} * {rhs} = {result} (carry={carry}, result_effectives={precision})")result = Number(result, precision, debug = self.debug or rhs.debug)return result #.round(precision)def __truediv__(self, rhs):"""除法运算符"""if not isinstance(rhs, Number):rhs = Number(rhs)assert rhs.value != 0, "Division By Zero"if rhs.debug:self.debugger = rhs.debuggerresult = self.value / rhs.valueprecision = min(self.effective_digits, rhs.effective_digits)self.debugger(f"{self} / {rhs} = {result} (result_effectives={precision})")result = Number(result, precision, debug = self.debug or rhs.debug)return result #.round(precision)def __floordiv__(self, rhs):"""整除运算符"""if not isinstance(rhs, Number):rhs = Number(rhs)assert rhs.value != 0, "Division By Zero"if rhs.debug:self.debugger = rhs.debuggerresult = self.value // rhs.valueprecision = min(self.effective_digits, rhs.effective_digits)self.debugger(f"{self} // {rhs} = {result} (precision={precision})")result = Number(result, precision, debug = self.debug or rhs.debug)return result #.round(precision)def __radd__(self, lhs):"""反向加法运算符"""return self + lhsdef __rsub__(self, lhs):"""反向加法运算符"""return Number(lhs) - selfdef __rmul__(self, lhs):"""反向乘法运算符"""return self * lhsdef __rtruediv__(self, lhs):"""反向除法运算符"""return Number(lhs) / selfdef __rfloordiv__(self, lhs):"""反向整除运算符"""return Number(lhs) // selfdef sqrt(self):"""平方根运算"""res = self.value.sqrt()return Number(res, self.effective_digits)def log10(self):"""以10为底的对数运算"""res = self.value.log10()result = res.quantize(Decimal('1e-'+str(self.effective_digits)), ROUND_HALF_EVEN)return Number(result)def __str__(self):"""字符串化"""return str(self.value)def __neg__(self):"""取负运算"""tmp = selftmp.value = -tmp.valuereturn tmpdef __repr__(self):"""表达形式化"""return self.value.__repr__()# %%
if __name__ == "__main__":a = Number('1', debug=True)b = Number('1.44')c = Number('2.25')p1, p2, p3 = Number("0.3"), Number("0.2"), Number(".5")print(a*p1+b*p2+c*p3)# c = 52.9e6 # 浮点类型,会收到警告# print(a*b*c/1e+9+10.00000) # 浮点类型,会收到警告pi = Number(math.pi, precision=12) # 浮点类型,会收到警告print(pi)pi = pi.round(2)print(pi)
# %%

写在最后:

她说所有要做大学物理实验的学生都要做这个东西,但是我们在网上怎么找也找不到能够满足要求的代码,大部分实现只实现了数值修约部分——然而如果你细看过程序,就会发现,Python的Decimal里面的quantize已经有所需要的舍入方法了(事实上,那里面什么都有!)。由于实在是找不到,而她又确实是有这样的需求,我那时(疫情期间嘛)比较闲,就干脆帮她做了,意外地并不难,第一版也就百来行。经过几次迭代,现在最终版本也只有近200行。

说实话很奇怪,如果有这么大的需求,难道不是应该早就有这样的代码了吗?但是我们俩实在找不到这样的代码。所以虽然可能和其他人撞车,但是我(隔了几个月之后)最终还是决定把它公开,能帮到多少人是多少人。

如果帮到了你,那最好咯。

数值修约程序(包括运算过程有效数字保留,Python3实现)相关推荐

  1. 数理统计(数值修约、0.5修约、0.2修约、有效数字运算、平均值、中位数、极差、标准差、变异系数)

    一.数值修约: 口诀:四舍六入五考虑,五后非零则进一,五后皆零看奇偶,奇进偶舍不连续. 1.15保留一位小数:1.15=1.2(五后非零 看五前面是奇数还是偶数,1是奇数,所以进1位=1.2) 1.1 ...

  2. 在VisualBasic6.0中实现0.5数值修约

    今天在编程的过程中,需要实现0.5数值修约,想了很长时间,总算研究出来了! 要求将计算结果进行0.5数值修约,并且保留一位小数.具体实现过程是这样的:首先将需要修约的数值保留两位小数,然后乘以2,保留 ...

  3. GB/T 8170-2008 数值修约规则与极限数值的表示和判定

    摘抄自规范 GB/T 8170-2008 数值修约规则与极限数值的表示和判定 3.2进舍规则 1.拟舍弃数字的最左一位数字小于5,则舍去,保留其余各位数字不变: 2.拟舍弃数字的最左一位数字大于5,则 ...

  4. python random.round 修约方法 4舍6入5看齐,奇进偶不进

    奇进偶不进 根据国家标准的有关规定,过去所采用的"四舍五入"的方法早已被"4舍6入5看齐,奇进偶不进"的方法所取代. 就是说,拟舍弃数小于5时,均应按" ...

  5. 修约函数,四舍六进五单双 的修约规则,给有需要的朋友参考

    首先来一个JAVASCRIPT 版本 JAVASCRIPT版修约函数  1 <!--  2 //################################################# ...

  6. C# 小数位修约(保留小数位有效数位)

    实现代码: /// <summary> /// 小数位修约 -- 四舍六入5成双,至少保留一位有效位(四舍六入五成双) /// 数值0.00512,保留2位,修约为0.005:0.0002 ...

  7. 有效数字修约,按四舍六入五成双原则

    # 定义函数修约小数位数,按四舍六入五成双的原则 def Little (x,y): # x是要修约的数,y是要保留的小数位数.ABS_x = abs(x) # 取x的绝对值str_x = str(A ...

  8. 题目: 某学校需要一个能给学领前儿童自动出三个数加减法的算术题的程序,要求在运算过程中只能使用20以内的加减法且不能出现负数,并且未知数可以在等式的任何一位, 如 3 + 4 – 2 = ( ), 8

    题目: 某学校需要一个能给学领前儿童自动出三个数加减法的算术题的程序,要求在运算过程中只能使用20以内的加减法且不能出现负数,并且未知数可以在等式的任何一位, 如 3 + 4 – 2 = ( ), 8 ...

  9. 浮点数修约的法则c语言,IEEE754浮点表示法详解

    前言 IEEE754是IEEE二进制浮点算术标准.这个标准定义了表示浮点数的常规值与非规格化值(denormal number),一些特殊值(infinity)和非数值(NaN), 以及这些数值的浮点 ...

最新文章

  1. 10款著名的代码(文本)编辑器
  2. boost::allocate_shared相关的测试程序
  3. SQL Server 2016 RC0 安装(超多图)
  4. 跟一个刚毕业不久的码农聊天后被凡尔赛了
  5. “CSDN开发助手”:【必备插件 · 安装与使用教程】
  6. python-魔法方法-attr系列方法
  7. Java图形界面设计——substance皮肤
  8. 造芯片这事情,到底要不要找潘金莲算账?
  9. 【ES】ES 如何在一个机器上同时模拟多个node
  10. 刺客信条奥德赛缺少dll文件_《刺客信条 奥德赛》免费归来,单人冒险暗杀游戏,搞一搞喽...
  11. 设计模式 (十八 ) 观察者模式
  12. cs231n softmax作业笔记
  13. 含泪入坑 GMSSL
  14. 犬类水疗跑步机的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  15. grok java_Java Grok.match方法代码示例
  16. 文本相似度:Distributed Representations of Sentences and Documents
  17. mac 打开html文件乱码,mac打开txt文件乱码解决方法
  18. 通信业的双11来了!充话费、办宽带、买手机每年这时候最划算
  19. BOF——Bag-of-Featrures
  20. HTML页面静态化技术

热门文章

  1. Java开发工程师的就业方向有哪些?
  2. Kindle 可旋转桌面时钟
  3. Rasa课程、Rasa培训、Rasa面试、Rasa实战系列之Finding Unexpected Intents
  4. Everyday English(四)
  5. Bootstrap按钮样式
  6. 亲戚(relation)
  7. div html用法详解,div标签详解
  8. 面部识别预处理全家桶:mtcnn人脸捕捉、人脸点对齐、仿射运算
  9. 2021年6月PMP考试50天备考5A通过经历心得分享
  10. 为什么重写Equals方法要重写HashCode方法