找回密码
 立即注册
搜索
查看: 4638|回复: 47

[软件] 学python第四天遇到一个奇怪的问题

[复制链接]
发表于 2022-5-5 00:00 | 显示全部楼层 |阅读模式
其中遇到一个怪事,有个list我的创建方法和答案不太一样
0是我的解法,1是标准答案
  1. possible_ms_1 = [a * 0.1 for a in range(-10, 11)]
  2. possible_ms_0 = [a / 10 for a in range(-10, 11)]

  3. print(possible_ms_1)
  4. print(possible_ms_0)
  5. print(possible_ms_1 == possible_ms_0)
复制代码
1号输出是这样的
  1. [-1.0, -0.9, -0.8, -0.7000000000000001, -0.6000000000000001, -0.5, -0.4, -0.30000000000000004, -0.2, -0.1, 0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9, 1.0]
复制代码
0号输出是这样的
  1. [-1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
复制代码
为什么!这尼玛不是一个意思么!!为什么会不一样!!

PS: 在做的是codecademy的小作业,直接导致了part2最后算出来的结果不一样
jupyter链接在这

https://static-assets.codecademy ... near-Regression.zip
有兴趣的可以把标准答案里的 "* 0.1",改成"/ 10"试试看

想了一晚上都没想通,郁闷睡觉



回复

使用道具 举报

     
发表于 2022-5-5 00:07 | 显示全部楼层
本帖最后由 13号 于 2022-5-5 00:11 编辑

不知道为啥,但是简单的说,float本来就是近似表达,看起来只是这两种计算方式下,表达的数据有区别。
你可以认为作为float时, -0.7000000000000001 和 -0.7 就是一个值。 "正确"的float判断是否相等的方式就是设置一个阈值,只要小于这个阈值就认为是同一个数。
回复

使用道具 举报

     
发表于 2022-5-5 00:07 来自手机 | 显示全部楼层
瞎猜的,你乘0.1相当于转浮点了,除以10还当整数?
编程语言里本来二进制数据放十进制算就回出现莫名其妙的零头。要不你换换用numpy 或者mathematia 试试。
回复

使用道具 举报

     
发表于 2022-5-5 00:10 | 显示全部楼层
nexus1 发表于 2022-5-5 00:07
瞎猜的,你乘0.1相当于转浮点了,除以10还当整数?
编程语言里本来二进制数据放十进制算就回出现莫名其妙的 ...

>>> list(map(type, possible_ms_1))
[<class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>]
>>> list(map(type, possible_ms_0))
[<class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>, <class 'float'>]

都是浮点(其实第二个不是浮点的话,直接就是0,1了,不会出现小数)
回复

使用道具 举报

发表于 2022-5-5 00:12 | 显示全部楼层
因为10是准确数,0.1是近似数
回复

使用道具 举报

     
发表于 2022-5-5 00:19 | 显示全部楼层
本帖最后由 littleneko 于 2022-5-5 00:21 编辑

IEEE754 浮点数的精度问题,0.1 已经不能被精确表示了,再说 float/double == 比较是没有意义的,看来 codecademy 里是用的 == 比较的浮点数的值,可以狠狠地批判一番了
回复

使用道具 举报

     
发表于 2022-5-5 00:22 来自手机 | 显示全部楼层
10是精确的10,0.1是个0.10000000001之类的玩意

— from motorola XT2125-4, Android 11 of S1 Next Goose v2.5.2-play
回复

使用道具 举报

     
发表于 2022-5-5 00:23 | 显示全部楼层
https://juejin.cn/post/7010416601711427615

楼上好像都没提到,楼主可能不知道这个知识点,先看看浮点数的精度问题吧
回复

使用道具 举报

     
发表于 2022-5-5 00:58 | 显示全部楼层
本帖最后由 革萌 于 2022-5-5 01:00 编辑


Screenshot from 2022-05-05 00-59-56.png
就是float算不准
回复

使用道具 举报

头像被屏蔽
     
发表于 2022-5-5 01:03 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

     
发表于 2022-5-5 01:11 | 显示全部楼层
https://0.30000000000000004.com/
这是个经典梗了。
所以浮点型判断,不要用等号,而应该用专门的isclose函数。
  1. >>> 0.1 + 0.2
  2. 0.30000000000000004
  3. >>> 0.1 + 0.2 == 0.3
  4. False
  5. >>> import math
  6. >>> import numpy as np
  7. >>> math.isclose(0.1+0.2, 0.3)
  8. True
  9. >>> np.isclose(0.1+0.2, 0.3)
  10. True
复制代码
回复

使用道具 举报

     
发表于 2022-5-5 01:50 | 显示全部楼层
兄弟这是之前没学过别的语言啊。。所有语言统统都有float问题,像java里就是用BigDecimal来解决算钱之类的高精度浮点
回复

使用道具 举报

头像被屏蔽
     
发表于 2022-5-5 02:26 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

     
发表于 2022-5-5 04:43 来自手机 | 显示全部楼层
kll85757 发表于 2022-5-5 02:26
我一直以为只有js有浮点运算的精度问题。。

不然为什么float和double的中文叫单精度浮点和双精度浮点
只要它还是二进制计算机就一定会有这样的问题
回复

使用道具 举报

     
发表于 2022-5-5 08:10 来自手机 | 显示全部楼层
本帖最后由 alann 于 2022-5-5 08:19 编辑

这个问题我也一直没完全搞明白,为什么1/10直接出来0.1,不是说0.1无法被被浮点精确表示吗?楼里已经有人验证了算出来的数是float型,既然是用float储存,为什么还能精确显示出0.1?
回复

使用道具 举报

     
发表于 2022-5-5 08:21 | 显示全部楼层
alann 发表于 2022-5-5 08:10
这个问题我也一直没完全搞明白,为什么1/10直接出来0.1,不是说0.1无法被被浮点精确表示吗?楼里已经有人验 ...

最简单的实现就是舍弃小于阈值的尾数,再依次去掉末尾的0,然后输出就是了啊。
回复

使用道具 举报

发表于 2022-5-5 08:43 | 显示全部楼层
alann 发表于 2022-5-5 08:10
这个问题我也一直没完全搞明白,为什么1/10直接出来0.1,不是说0.1无法被被浮点精确表示吗?楼里已经有人验 ...

因为大部分语言显示浮点数都会自动rounding。

rounding 方式方法都不尽相同,屏幕显示数和实际数也多少有些出入,显示相同的数也不一定实际相同。
回复

使用道具 举报

     
发表于 2022-5-5 09:12 来自手机 | 显示全部楼层
kll85757 发表于 2022-5-5 02:26
我一直以为只有js有浮点运算的精度问题。。

乆都是JS的错啦

—— 来自 Xiaomi Redmi K30 5G, Android 11上的 S1Next-鹅版 v2.5.4
回复

使用道具 举报

发表于 2022-5-5 09:27 | 显示全部楼层
学了点pandas来处理表格,就发现这个问题了,原来是这个原因,顺道问一下,一般这种情况标准是怎么处理的?我都是每次round一下
回复

使用道具 举报

     
发表于 2022-5-5 09:42 | 显示全部楼层
接着用就是了,你又不造火箭在乎这点误差?或者你学python是为了输出数值龟龟整整的好看?
回复

使用道具 举报

 楼主| 发表于 2022-5-5 09:43 来自手机 | 显示全部楼层
大概理解了
那为什么这两个list里面
调用每一个去==对比
像-1.0 -0.9这些都没事是true,偏偏-0.7就有问题false呢?

—— 来自 OnePlus KB2005, Android 12上的 S1Next-鹅版 v2.5.4
回复

使用道具 举报

发表于 2022-5-5 09:56 | 显示全部楼层
很简单,浮点数有精度位数
有些浮点运算后二进制表示是一致的,就是相等
有些浮点运算会导致二进制表示跟直接写出来的浮点数不等,那就失败
回复

使用道具 举报

头像被屏蔽
     
发表于 2022-5-5 10:02 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

     
发表于 2022-5-5 10:18 来自手机 | 显示全部楼层
所以是不是/10的这些数实际也带了很长的尾巴,只是这些变量有个打印时自动rounding的开关是开的,而乘0.1算出来的变量的这个开关是关的。
回复

使用道具 举报

发表于 2022-5-5 10:21 | 显示全部楼层
本帖最后由 tsubasa9 于 2022-5-5 10:26 编辑
alann 发表于 2022-5-5 10:18
所以是不是/10的这些数实际也带了很长的尾巴,只是这些变量有个打印时自动rounding的开关是开的,而乘0.1算 ...

不是,这是浮点数的ieee表示的问题,不是什么rounding不rounding的问题

在ieee表示下浮点数的乘法和除法的过程,和人类习惯的运算方法并不相同,导致结果不一致

回复

使用道具 举报

     
发表于 2022-5-5 10:25 来自手机 | 显示全部楼层
tsubasa9 发表于 2022-5-5 10:21
不是,这是浮点数的ieee表示的问题,不是什么rounding不rounding的问题

那还是1/10和1*0.1在内存中的储存值不一样?虽然类型都是float?
回复

使用道具 举报

发表于 2022-5-5 10:27 | 显示全部楼层
alann 发表于 2022-5-5 10:25
那还是1/10和1*0.1在内存中的储存值不一样?虽然类型都是float?

浮点数的乘法和除法在ieee表示下并不是通常的那种,并不是精确的倒数关系
回复

使用道具 举报

     
发表于 2022-5-5 11:07 来自手机 | 显示全部楼层
本帖最后由 superlattice 于 2022-5-5 11:12 编辑
alann 发表于 2022-5-5 10:25
那还是1/10和1*0.1在内存中的储存值不一样?虽然类型都是float?

二进制乘除法和十进制乘除法实现不一样,不是互为逆运算

如果按楼上都指定十进制运算就没事,解释器内部会用别的逻辑

如果一个11进制的外星人脑控地球人(只会进制转换和10进制运算)做浮点运算,也会出现类似的问题

【【官方双语】我试图搞坏这台100万刀的电脑 - 逛逛IBM Z16大型机#linus谈科技-哔哩哔哩】 https://b23.tv/dDNk6lL

这玩意内部有原生10进制BCD码运算器,普通二进制计算机没有,也是制霸金融业这种快算十进制数需求的原因之一。
回复

使用道具 举报

     
发表于 2022-5-5 11:18 来自手机 | 显示全部楼层
superlattice 发表于 2022-5-5 11:07
二进制乘除法和十进制乘除法实现不一样,不是互为逆运算

如果按楼上都指定十进制运算就没事,解释器内部 ...

可是不管过程怎么计算,终归结果还是用float储存的吧,说明float也是可以精确储存0.1的?
回复

使用道具 举报

发表于 2022-5-5 11:19 | 显示全部楼层
本帖最后由 tsubasa9 于 2022-5-5 11:22 编辑
alann 发表于 2022-5-5 11:18
可是不管过程怎么计算,终归结果还是用float储存的吧,说明float也是可以精确储存0.1的? ...

单就这个问题,不行
0.1用ieee表示末尾有一串小数,只是不显示而已
虽然楼上讲了一堆,但进制并不是计算有误差的原因

顺便这个网址可以看浮点的ieee表示,以及误差
https://www.h-schmidt.net/FloatConverter/IEEE754.html

回复

使用道具 举报

     
发表于 2022-5-5 11:23 来自手机 | 显示全部楼层
alann 发表于 2022-5-5 11:18
可是不管过程怎么计算,终归结果还是用float储存的吧,说明float也是可以精确储存0.1的? ...

https://blog.csdn.net/weixin_39986543/article/details/110656404

0.1不是二进制整数,不能被精确表示

评分

参与人数 1战斗力 +2 收起 理由
cxn + 2 好评加鹅

查看全部评分

回复

使用道具 举报

     
发表于 2022-5-5 11:36 来自手机 | 显示全部楼层
如果1/10和1*0.1都有尾巴,那岂不是又回到我前面的假设,变量有个隐藏的print显示时自动rounding的开关,1/10的变量这个隐藏开关是打开的,1*0.1的变量这个隐藏开关是关的。
回复

使用道具 举报

发表于 2022-5-5 11:40 | 显示全部楼层
alann 发表于 2022-5-5 11:36
如果1/10和1*0.1都有尾巴,那岂不是又回到我前面的假设,变量有个隐藏的print显示时自动rounding的开关,1/ ...

反正都不准,何必纠结这个,换个cpu结果可能又变了。

CPU和软件的具体实现都可以造成影响。
回复

使用道具 举报

发表于 2022-5-5 11:40 | 显示全部楼层
alann 发表于 2022-5-5 11:36
如果1/10和1*0.1都有尾巴,那岂不是又回到我前面的假设,变量有个隐藏的print显示时自动rounding的开关,1/ ...

谁告诉你1.0的显示有尾巴,这里不都在说主楼的那几个么
实际上所有显示都有rounding,包括你看到的3.000……004也隐藏了尾巴
回复

使用道具 举报

发表于 2022-5-5 11:45 | 显示全部楼层
alann 发表于 2022-5-5 11:36
如果1/10和1*0.1都有尾巴,那岂不是又回到我前面的假设,变量有个隐藏的print显示时自动rounding的开关,1/ ...

而且与其在这里汴京不如直接去看浮点数ieee表示以及乘除法实现原理
回复

使用道具 举报

     
发表于 2022-5-5 12:13 来自手机 | 显示全部楼层
本帖最后由 RJG丶one 于 2022-5-5 12:26 编辑

应该就是精度的问题。10能被精确表示,0.1不能。
想象一下,0.1具有1e-7的精度,乘了7约等于乘了10,精度被放大到了1e-6,而float就6位有效数字,是不是明显会有问题?
正常的结果只能说是碰巧误差没有放大或者放大后还落在有效区间内。
回复

使用道具 举报

 楼主| 发表于 2022-5-5 12:45 | 显示全部楼层
本帖最后由 诶哟卧草 于 2022-5-5 12:48 编辑

其实我觉得4.9999999 还是5还是5.0000001也无所谓了,目前我还没有计算机思维感觉这几个数都一样。
但我最好奇的是,为什么用在代码里最后造成的运算结果差异会那么大。
就是我丢的下载链接里的Reggie_Linear_Regression_Solution.ipynb这个,直接丢进vs code里run all就行
答案用的* 0.1,运算结果是
best_m 0.30000000000000004
best_b 1.7000000000000002
smallest_error  4.999999999999999

我用的是/ 10,结果是
best_m: 0.4, best_b: 1.6, smallest_error: 5.0

我感觉我才是正确答案啊,codecademy的答案不对
smallest_error差不多,但是m和b就差的有些多了

懒得下载的话我直接把代码贴出来大家可以跑跑看,就是根据一些坐标来统计线性回归的一个小作业
  1. def get_y(m, b, x):
  2.   y = m*x + b
  3.   return y

  4. def calculate_error(m, b, point):
  5.   y_point = get_y(m, b, point[0])
  6.   return abs(point[1] - y_point)

  7. def calculate_all_error(m ,b ,datapoints):
  8.     total_error = 0
  9.     for dp in datapoints:
  10.         error = calculate_error(m ,b ,dp)
  11.         total_error += error
  12.     return total_error

  13. possible_ms = [a * 0.1 for a in range(-100, 101)]
  14. possible_bs = [a * 0.1 for a in range(-200, 201)]
  15. # possible_ms = [a / 10 for a in range(-100, 101)]
  16. # possible_bs = [a / 10 for a in range(-200, 201)]

  17. smallest_error = float("inf")
  18. datapoints = [(1, 2), (2, 0), (3, 4), (4, 4), (5, 3)]

  19. for m_item in possible_ms:
  20.     for b_item in possible_bs:
  21.         new_error = calculate_all_error(m_item, b_item, datapoints)
  22.         if new_error < smallest_error:
  23.             smallest_error = new_error
  24.             best_b = b_item
  25.             best_m = m_item
  26.             
  27. print(f"best_m: {best_m}, best_b: {best_b}, smallest_error: {smallest_error}")
复制代码
回复

使用道具 举报

     
发表于 2022-5-5 13:05 | 显示全部楼层
你猜猜为什么皮衣黄会吹嘘自家的双精度浮点??
回复

使用道具 举报

     
发表于 2022-5-5 13:23 来自手机 | 显示全部楼层
a是-10到11,除数是10,为啥除出来是小数

—— 来自 vivo V1981A, Android 11上的 S1Next-鹅版 v2.5.2-play
回复

使用道具 举报

头像被屏蔽
     
发表于 2022-5-5 13:37 来自手机 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|上海互联网违法和不良信息举报中心|网上有害信息举报专区|962110 反电信诈骗|举报电话 021-62035905|Stage1st ( 沪ICP备13020230号-1|沪公网安备 31010702007642号 )

GMT+8, 2024-11-14 19:56 , Processed in 0.275996 second(s), 7 queries , Gzip On, Redis On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表