一减去小数等于一?处理小数问题

0 投票
2 回答
1377 浏览
提问于 2025-04-17 21:02

标题已经说明了问题。这里发生了什么?我该怎么做才能避免这种情况?我真的需要改变我所有的单位(这是一个物理问题),只是为了得到一个足够大的结果,以至于Python不会把1-x四舍五入到1吗?

代码:

import numpy as np
import math

vel=np.array([5e-30,5e-30,5e-30])

c=9.7156e-12

def mag(V):
    return math.sqrt(V[0]**2+V[1]**2+V[2]**2)

gam=(1-(mag(vel)/c)**2)**(-1/2)

print(mag(vel))
print(mag(vel)**2)
print(mag(vel)**2/(c**2))
print(1-mag(vel)**2/(c**2))
print(gam)

输出:

>>> (executing lines 1 to 17 of "<tmp 1>")
8.660254037844386e-30
7.499999999999998e-59
7.945514251743055e-37
1.0
1.0
>>> 

2 个回答

0

我觉得你可能得不到你期待的结果,因为你遇到了计算机数学的限制。关于这种计算,没人能完全避免这个错误,除非你找到一些理论上有无限小数的模型,并且可以用它们进行运算。如果这对你要解决的问题来说太复杂了,或许你只需要小心一点,尽量处理好这些计算中的错误。

网上有很多书籍和资料,提供了不同的方法来处理计算中的错误,这些方法可以帮助你减少错误,而不是完全避免它们。

希望我的回答能对你有所帮助,不会让你失望。

2

在Python中,decimal模块可能会有用,另外mpmath也可以尝试。

关于这个问题,可以参考这篇SO的文章

如果你愿意使用Java(而不是Python),你可以使用BigDecimal,或者apfloat,还有JScience

例如,8.66e-30这个数字只有3位有效数字,但如果要表示1减去这个数,就需要超过30位有效数字。如果超过16位有效数字,你就需要用其他方式来表示这些数字,比如用很长的字符串。不过,用长字符串做数学运算会比较麻烦。你也可以在很长的字节数组上进行二进制计算。这些字节值可以表示一个非常大的整数,并且可以通过你选择的缩放因子进行调整。所以,如果你能支持一个大于1E60的整数值,你可以调整这个值,使得1E-60可以用最大值1来表示。大约用200位或25个字节就能做到,而用400位的话,你应该能精确表示从1E60到1E-60的整个范围。可能已经有一些工具可以进行这种计算,特别是那些在数学或安全领域工作的人,他们可能需要表示PI到千位小数,这用双精度浮点数是做不到的。

另一个有用的技巧是使用缩放因子。也就是说,在你原来的坐标空间中,你不能进行减法,因为数字无法表示这些值。但是,如果你假设在进行小调整时不同时关心大调整,那么你可以对数据进行变换。例如,你可以从你的数字中减去1。这样,你就可以把1-1E-60表示为-1E-60。在你的变换空间中,你可以非常精确地进行许多操作,但要清楚地知道,如果你试图将它们从变换空间转换回去,它们会因为不相关而丢失。这种策略在放大地图时很有用。在进行单精度浮点DirectX计算时,微米级别的调整在经纬度单位上是行不通的。但你可以在放大时临时改变你的缩放比例,这样操作就能正常进行。

因此,复杂的数字可以用一个大数字加上一个表示小规模调整的第二个数字来表示。例如,如果你在双精度浮点数中有16位数字,你可以用第一个数字表示值的大部分,比如从1到1E16,而第二个双精度浮点数则表示额外的小部分。不过,使用16位数字可能会让双精度浮点数在表示大值时出现误差,所以你可能只用15位或14位来确保安全。

1234567890.1234567890

变成

1.234567890E9 + 1.23456789E-1.

基本上,精度越高,你的复杂数字就会有更多的项。但是,当每一项在数学上相对独立时,这种方法效果不错,但如果你需要进行大量跨尺度的严格计算,管理这些值之间的关系可能会比它的价值更麻烦。

撰写回答