bcadd 的精度问题?

以为是集合 sum 精度有问题,实际是bcadd的用法不正确。
这里有一个计算

$data = [
    [
        "id" => 9,
        "user_id" => 2,
        "value" => 0.000013
    ],
    [
        "id" => 19,
        "user_id" => 2,
        "value" => 0.000009
    ]
];
$sumValue = collect($data)->sum('value');
\Log::info('$sumValue:' . $sumValue);
// 结果:$sumValue:2.2E-5  是科学记数法
\Log::info('bcadd:' . bcadd($sumValue, 0, 8));
// 结果: bcadd:0.00000000  
\Log::info('sprintf:' . sprintf('%.8f', $sumValue));
// 结果:sprintf:0.00002200

bcadd 数字、字符串、科学记数法类型的计算结果

1、数值
1) 4位小数,且前4位为0的
\Log::info('bcadd:' . bcadd(0.00002200, 0, 8));
// 结果:bcadd:0.00000000
2) 5位小数,且前4位有大于0的
\Log::info('bcadd:' . bcadd(0.00012200, 0, 8));
// 结果:bcadd:0.00012200
2、字符串
\Log::info('bcadd:' . bcadd('0.00002200', 0, 8));
// 等同于 \Log::info('bcadd:' . bcadd((string)0.00012200, 0, 8)); 
// 结果:bcadd:0.00002200
3、科学记数法
1) 5位小数,且前4位有大于0的
\Log::info('bcadd:' . bcadd(2.2E-4, 0, 8));
// 结果:bcadd:0.00022000
2) 5位小数,且第4位等于0
\Log::info('bcadd:' . bcadd(2.2E-5, 0, 8));
// 结果:bcadd:0.00000000
3) 5为小数,第4位大于0
\Log::info('bcadd:' . bcadd(12.2E-5, 0, 8));
// 结果:bcadd:0.00012200

结论是超过4位小数,且前4位都为0时,如不转为字符串进行bcadd计算,那么精度会有问题,结果会按0来计算;高精度计算,建议先转为字符串再进行计算。

根据需要,提前转为精度+1的字符串类型参与计算,如需保留6位,就取7位出来
$value = 0.000011;
$value = sprintf('%.7f', $value);
保存数据时,可转再转为6位
$model->value = bcadd($value, 0, 6);
$model->save();

注:laravel 从mysql读出的decimal类型的数据会转换为字符串类型,所以直接从数据库查询结果中取到的此类型字段可以直接参与bc函数的计算。
默认使用mysqli 或者 pdo-mysql从数据库中读出来的数据都会转换为字符串类型,通过设置参数可以把int/float/tinyint等会转为对应数据类型。laravel默认设置了这个转换参数,如下图:

bcadd 的精度问题?
参考:分享:Laravel 的 DB 查询是如何实现字段类型自动转的?
详细解释:
博客: Laravel Database——数据库服务的启动与连接
数据库驱动参考:
https://www.jianshu.com/p/7a9c0bb8fe22
https://blog.csdn.net/weixin_34406061/arti...

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 4

@lovecn 是的,参数是字符串类型,但是传4位以下小数(数值类型的)进去计算结果是正常的,不过还是文档写的来好

4年前 评论

感谢楼主,之前一直用这个函数,因为一直是计算两位小数位的数字,虽然没有出现数据不对的情况,但通过楼主的文章学到了

4年前 评论

@老衲爱饮酒 :grinning: 一起学习,一起进步哈

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!