这是一个有趣的问题,我试图在前几天工作。是否可以强制一个float
的有效位数或指数与Python中的另一个float
相同?
问题出现了,因为我试图重新缩放某些数据,以便min和max匹配另一个数据集。但是,我重新调整后的数据略有偏差(大约小数点后6位),这足以引起问题。
要提出一个想法,我有f1
和f2
(type(f1) == type(f2) == numpy.ndarray
)。我想要np.max(f1) == np.max(f2) and np.min(f1) == np.min(f2)
。为此,我做了:
import numpy as np
f2 = (f2-np.min(f2))/(np.max(f2)-np.min(f2)) # f2 is now between 0.0 and 1.0
f2 = f2*(np.max(f1)-np.min(f1)) + np.min(f1) # f2 is now between min(f1) and max(f1)
结果(仅作为示例)将是:
np.max(f1) # 5.0230593
np.max(f2) # 5.0230602 but I need 5.0230593
我最初的想法是强制float
的指数将是正确的解决方案。我无法找到它,所以我根据自己的需要制定了解决方法:
exp = 0
mm = np.max(f1)
# find where the decimal is
while int(10**exp*mm) == 0
exp += 1
# add 4 digits of precision
exp += 4
scale = 10**exp
f2 = np.round(f2*scale)/scale
f1 = np.round(f1*scale)/scale
现在np.max(f2) == np.max(f1)
但是,还有更好的方法吗?我做错什么了吗?是否有可能将float
重塑为与另一个float
(指数或其他方式)相似?
编辑:正如所建议的那样,我现在正在使用:
scale = 10**(-np.floor(np.log10(np.max(f1))) + 4)
虽然我的上述解决方案可行(对于我的应用程序),但我有兴趣知道是否有某种解决方案可以某种方式强制float
拥有相同的指数和/或有效数字数字将变得相同。
答案 0 :(得分:7)
这取决于你所说的“尾数。”
在内部,浮点数在基数2中使用科学记数法存储。因此,如果你的意思是 base 2 尾数,它实际上很容易:只乘以或除以2的幂(不是10的幂) ),并且尾数将保持不变(假设指数不超出范围;如果确实如此,您将被钳制到无穷大或零,或者根据建筑细节可能进入denormal numbers。重要的是要了解当您重新调整2的幂时,小数扩展将不匹配。这是用这种方法保留的二进制扩展。
但是,如果你的意思是基数为10的尾数,不是,浮点数是不可能的,因为重新调整的值可能无法准确表示。例如,1.1不能在基数2(具有有限数字的位数)中精确表示,其方式与1/3不能在基数10中表示(具有有限的数字位数)。因此,将1/10重新缩小1/10不能完全准确地完成:
>>> print("%1.29f" % (11 * 0.1))
1.10000000000000008881784197001
但是,您可以使用decimal
来执行后者。小数在基数10中起作用,并且在基数10重新缩放方面将按预期运行。它们还提供了相当多的专用功能来检测和处理各种精度损失。但是小数don't benefit from NumPy speedups,所以如果您要处理大量数据,它们可能对您的用例来说效率不高。由于NumPy依赖于对浮点的硬件支持,并且大多数(所有?)现代架构都没有为基础10提供硬件支持,因此这不容易解决。
答案 1 :(得分:3)
尝试用
替换第二行f2 = f2*np.max(f1) + (1.0-f2)*np.min(f1)
说明:有两个地方可能会出现差异:
步骤1)f2 = (f2-np.min(f2))/(np.max(f2)-np.min(f2))
当您检查np.min(f2)
和np.max(f2)
时,您是否得到0和1或类似于1.0000003的内容?
第2步)f2 = f2*(np.max(f1)-np.min(f1)) + np.min(f1)
由于舍入错误,像(a-b)+b
这样的表达式并不总是精确生成a
。建议的表达式稍微稳定一些。
有关详细说明,请参阅 David Goldberg What Every Computer Scientist Should Know About Floating-Point Arithmetic。
答案 2 :(得分:2)
TL; DR
使用
f2 = f2*np.max(f1)-np.min(f1)*(f2-1) # f2 is now between min(f1) and max(f1)
并确保您使用双精度,通过查看绝对或相对差异来比较浮点数,避免舍入以调整(或比较)浮点数,并且不要设置底层组件手动浮点数。
详细
正如您所发现的,这不是一个非常容易复制的错误。但是,使用浮动数字可能会出错。例如,将1 000 000 000 + 0 . 000 000 000 1
加在一起会得到1 000 000 000 . 000 000 000 1
,但即使对于双精度(支持15 significant figures左右),这也是太多有效数字,因此会删除尾随小数。而且,有些"短"正如@ Kevin&{39} answer所述,数字无法准确表示。有关详情,请参阅,例如here。 (搜索"浮点截断舍入错误"更多内容。)
这是一个证明问题的例子:
import numpy as np
numpy.set_printoptions(precision=16)
dtype=np.float32
f1 = np.linspace(-1000, 0.001, 3, dtype=dtype)
f2 = np.linspace(0, 1, 3, dtype=dtype)
f2 = (f2-np.min(f2))/(np.max(f2)-np.min(f2)) # f2 is now between 0.0 and 1.0
f2 = f2*(np.max(f1)-np.min(f1)) + np.min(f1) # f2 is now between min(f1) and max(f1)
print (f1)
print (f2)
输出
[ -1.0000000000000000e+03 -4.9999951171875000e+02 1.0000000474974513e-03]
[ -1.0000000000000000e+03 -4.9999951171875000e+02 9.7656250000000000e-04]
关注@Mark Dickinson的comment,我使用了32位浮点数。这与您报告的错误一致,相对误差约为10 ^ -7,大约在第7位有效数字
In: (5.0230602 - 5.0230593) / 5.0230593
Out: 1.791736760621852e-07
转到dtype=np.float64
会让事情变得更好,但它仍然不是完美的。然后上面的程序给出了
[ -1.0000000000000000e+03 -4.9999950000000001e+02 1.0000000000000000e-03]
[ -1.0000000000000000e+03 -4.9999950000000001e+02 9.9999999997635314e-04]
这并不完美,但通常足够接近。在比较浮点数时,您几乎从不想使用严格相等,因为如上所述可能存在小错误。而是从另一个中减去一个数字并检查绝对差值是否小于某个容差,和/或查看相对误差。例如,参见numpy.isclose
。
回到你的问题,似乎应该可以做得更好。毕竟,f2
的范围是0到1,因此您应该能够在f1
中复制最大值。问题出在
f2 = f2*(np.max(f1)-np.min(f1)) + np.min(f1) # f2 is now between min(f1) and max(f1)
因为当f2
的元素为1时,您要做的不仅仅是将1乘以f1
的最大值,导致出现浮点算术错误的可能性。请注意,您可以将括号f2*(np.max(f1)-np.min(f1))
乘以f2*np.max(f1) - f2*np.min(f1)
,然后将得到的- f2*np.min(f1) + np.min(f1)
分解为np.min(f1)*(f2-1)
给出
f2 = f2*np.max(f1)-np.min(f1)*(f2-1) # f2 is now between min(f1) and max(f1)
因此,当f2
的元素为1时,我们会1*np.max(f1) - np.min(f1)*0
。相反,当f2
的元素为0时,我们有0*np.max(f1) - np.min(f1)*1
。数字1和0 可以准确表示,因此不应有错误。
修改后的程序输出
[ -1.0000000000000000e+03 -4.9999950000000001e+02 1.0000000000000000e-03]
[ -1.0000000000000000e+03 -4.9999950000000001e+02 1.0000000000000000e-03]
即。根据需要。
尽管如此,我仍然强烈建议只使用不精确的浮点比较(如果需要,可以使用紧密边界),除非你有充分的理由不这样做。浮点运算中可能会出现各种细微的错误,避免它们的最简单方法是永远不要使用精确的比较。
上面给出的另一种方法,可能更可取的方法是将两个数组重新缩放到0到1之间。这可能是在程序中使用的最合适的形式。 (如果需要,两个数组都可以乘以缩放因子,例如f1
的原始范围。)
重新使用舍入来解决您的问题,我不推荐这个。四舍五入的问题 - 除了不必要地降低数据准确性这一事实 - 非常接近的数字可以在不同的方向上进行。 E.g。
f1 = np.array([1.000049])
f2 = np.array([1.000051])
print (f1)
print (f2)
scale = 10**(-np.floor(np.log10(np.max(f1))) + 4)
f2 = np.round(f2*scale)/scale
f1 = np.round(f1*scale)/scale
print (f1)
print (f2)
输出
[ 1.000049]
[ 1.000051]
[ 1.]
[ 1.0001]
这与以下事实有关:尽管讨论与这么多有效数字相匹配的数字很常见,但人们实际上并没有在计算机上这样比较它们。您计算差值然后除以正确的数字(对于相对误差)。
重做mantissas和exponents,请参阅math.frexp
和math.ldexp
,记录here。我不建议你自己设置这些(考虑两个非常接近但具有不同指数的数字,例如 - 你真的想设置尾数)。如果你想确保数字完全相同(并且类似于最小值),那么直接将f2
的最大值明确地设置为f1
的最大值要好得多。
答案 3 :(得分:0)
def rescale(val, in_min, in_max, out_min, out_max):
return out_min + (val - in_min) * ((out_max - out_min) / (in_max - in_min))
value_to_rescale = 5
current_scale_min = 0
current_scale_max = 10
target_scale_min = 100
target_scale_max = 200
new_value = rescale(value_to_rescale, current_scale_min, current_scale_max, target_scale_min, target_scale_max)
print(new_value)
new_value = rescale(10, 0, 10, 0, 100)
print(new_value)
answer:
150 100
答案 4 :(得分:-2)
这是一个带小数的
from decimal import Decimal, ROUND_05UP
num1 = Decimal('{:.5f}'.format(5.0230593)) ## Decimal('5.02306')
num2 = Decimal('{}'.format(5.0230602)) ## Decimal('5.0230602')
print num2.quantize(num1, rounding=ROUND_05UP) ## 5.02306
编辑**我对为什么会得到如此多的负面反馈感到有些困惑,所以这是另一种不使用小数的解决方案:
a = 5.0230593
b = 5.0230602
if abs(a - b) < 1e-6:
b = a