以科学记数法表示的浮点数类
我的应用需要跟踪 float 数字,采用科学计数法的格式(例如 1.23E4)——数据的导入导出也使用这种格式。所以我写了一个简单的 dataclass:
@dataclass
class SciNotation:
significand: float
exponent: int
def as_float(self) -> float:
if self.significand == 0:
return 0.0
return float(self.significand * 10 ** self.exponent)
随后我想增加对算术和比较运算符的支持,但希望既能复用现有的 float 函数,又不想为每一个 dunder 方法分别编写实现,于是我给出了如下实现:
@classmethod
def handle_dunders(cls, self: SciNotation, other: SciNotation | float | int):
# get name of caller method
import traceback
method_name = traceback.extract_stack()[-2].name
# get method of normal float
float_method = getattr(float, method_name)
# convert second operand if needed
if isinstance(other, SciNotation):
other = other.as_float()
# do the deed
result = float_method(self.as_float(), other)
if isinstance(result, float):
s, e = f"{result:E}".split("E")
return cls(significand=float(s), exponent=int(e))
elif isinstance(result, bool):
return result
else:
raise NotImplementedError
def __add__(self, other: Self | float | int):
return self.handle_dunders(self, other)
def __sub__(self, other: Self | float | int):
return self.handle_dunders(self, other)
def __mul__(self, other: Self | float | int):
return self.handle_dunders(self, other)
def __truediv__(self, other: Self | float | int):
return self.handle_dunders(self, other)
def __eq__(self, other: Self):
return self.handle_dunders(self, other)
def __lt__(self, other: Self):
return self.handle_dunders(self, other)
现在这个实现确实能工作,但看起来有点儿…… 诡异。有没有更好的办法来处理这个问题?
解决方案
你完全可以彻底颠倒你的模型的工作方式。不是把数字以科学计数法存储,然后在运算时再把它们转换成浮点数进行运算;而是直接以浮点数存储,在导出时再把它们转换为科学计数法。如你所知,'e' 格式说明符 将为你完成这一步:
>>> n = 12345.6789
>>> f"{n:e}"
'1.234568e+04'
>>> f"{n:.2e}"
'1.23e+04'
这可能会让你的代码更高效,因为你只需要在导入和导出时进行转换,而不是在每次运算时都进行转换。
你甚至可以通过对 float 进行子类化并提供一个对 __str__ 的重写来调用该格式说明符,但那样你大概还是得重载所有的运算操作。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。