热更新方案里 setVerifyCallback 回调得到的 asset.size 是不是有精度问题

10M以上的文件用size作为判断条件好像就会对不上了,打印出来的asset.size值和project.manifest中存的值会不同,getFileSize获取到的是精确的值。
粗略的看了一下asset.size用的是float,超过七位大概是10M是不是就会精度丢失了啊,不过不太懂c++不知道是不是这个原因造成的。
这么多年用下来的方案,看了论坛里面没人反应过这个问题,最佳实践是都只用md5做判断吗,还是说不应该有10M以上的文件。

1赞

截止到今日(2025年10月27日),这个问题还在,我本打算使用size代替md5做文件效验,毕竟获取md5要慢很多。我的文件大小为17.3M,符合楼主提到的大于10M说法,我当前使用的最新的官方版本:3.8.7,官方一直都没有修复这个问题,通过setVerifyCallback回调中获取到的size比我打到project.manifest中的size多1,我理解的是,文件大小使用的是字节单位,不应该直接用整数类型就可以吗?另外一个疑惑点,回调中返回的size数值,应该直接从project.manifest中获取就可以了,为什么会出现数值不一致的情况呢?

通过查看C++源码,找到了问题的根源:int转float过程中,导致数据精度丢失。具体文件位置如下:
C:\ProgramData\cocos\editors\Creator\3.8.8\resources\resources\3d\engine\native\extensions\assets-manager\Manifest.cpp 中 Manifest::Asset Manifest::parseAsset(const std::string &path, const rapidjson::Value &json){} 函数中,关键代码如下:

突然发现这个平台编辑器也有bug,会自动去掉代码中的尖括号,太坑爹了,只能用图片展示代码:
image
asset.size = static_cast(json[KEY_SIZE].GetInt()); // 这行代码中的尖括号被平台编辑器自动去掉了

测试中原始size=32515387,转成float后的值为:32515388;

当size的原始大小超过16777217(大约16M)的时候就会出现精度丢失的情况,具体原因如下:

1、float 的尾数只有 23 位,超过的部分会被截取掉;

2、截取需要遵循舍入规则(就近舍入,偶舍入),并不是简单截取舍弃掉,这也说明了为什么精度丢失后数值反而变大了;

3、拿数字32515387(对应文件大小为:31.0 MB)来举例说明,它转成float对应的格式为:1.11110000001001010011110 × 2^24,如果不截取,格式为:1.111100000010010100111011 × 2^24,正因为舍弃过程中产生了进位(具体操作细节自行网络搜索),然后将截取后的float值转回int类型,得到:1.11110000001001010011110 × 2^24 = 32515388,从而比实际值多了1;

解决方案:

1、可以将float类型换成double类型;

2、可以将float类型换成uint32_t类型,可以支持单文件大小为:4GB,基本上也够用了;

具体修改文件位置如下:

位置1:extensions/assets-manager/Manifest.h

将DownloadUnit结构体和ManifestAsset结构体中的size类型从float改成double或者uint32_t类型;

位置2:extensions/assets-manager/Manifest.cpp

将函数:Manifest::Asset Manifest::parseAsset(const std::string &path, const rapidjson::Value &json)中:
image

1赞