你必须知道的——double转换int的问题

问题

问题出自一个价格的转换需要将double类型转换(元)为integer类型(单位分)的数.

$salePrice = 10.2;
$price =(int) ($salePrice  * 100); // 1019

虽然少了一分钱,但是对于这种问题的严重性,每个用户少付款一分钱,虽然说平台用户少,但对于对账会触发报警,甚至不能下单因为实际付款额与售价不等.

接下来我们先从计算机如何存储二进制数字说起.

二进制数字与十进制数字转换

十进制转二进制

整数部分 --> 转为二进制
小数点不动
小数部分 --> 转为二进制

这里重点说下:小数部分转换

0.25转换为二进制如下:

0.25 * 2 = 0.5   // 0 取整数位如果为1取1,这里为0取0,小数部分不为0所以 拿0.5去下一步
0.5*2     =1.0     // 1 小数部分为0 结束计算

10.25就可以表示为 1010.01

二进制转十进制

小数部分转换:

0.25

0 1

得出小数部分算法

0*2^{-1} +  1*2^{-2}

double类型在内存空间模型

众所周知,php使用C语言实现,浮点类型在zval中是double.

符号位     11位 指数      52位尾数
0      0000 0000 000     000.....000

以10.25为例:

1010.01 => 转换为

1.01001 * 2^{3}
  • 将符号存储至符号位,0整数,1负数
  • 将2的指数加中间数存入指数位:3+127=130
  • 将小数位存储到52位尾数位当中.
  • 整数1不需要存储.

中间数:为了表示负指数,所以提出了一个中间数的概念. 中间数:2^n-1 n为指数位数.
所以double类型的中间数为1023, 而float类型为127.
例如指数为-7那么, 127 + (-7)= 120 -> 01111000,读取时候还得减去127,得出真实值.
中间数可以理解为一个符号位,表示指数的符号.
中间数全为1,尾数位为全为0表示无穷大
中间数全为1,尾数位不全为0表示NaN
中间数全为0,尾数位全为0表示0

因此得到内存模型为:

符号位     11位 指数      52位尾数位
0      1000 0000 011     01001 0......000

从内存模型当中读取数据时:

符号位     11位 指数                              52位尾数位
0      1000 0000 011                            01001 0......000
+     130-127 = 0000 0000 011= 3    
尾数位前补1和. => 1.01001 
将小数点右移指数位个数(减去中间数后如果指数位最低位为1则左移) => 1010.01
按照二进制转十进制规则得出:10.25

解决方案

PHP中可以使用bcmath库,避免精度丢失.

两数相乘可以用bcmul这个函数.

(int)bcmul(10.2, 100) =>  1020  

使用bccomp准确比较两个任意精度类型.

其他语言参照相关的库,C语言中也有bcmath库的实现.

在涉及到跟金额相关的业务时候推荐最好这么做,避免产生类似不安全的问题.

  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
多项式转换是指将一组多项式系数表示的函数转换为另一组多项式系数表示的函数,常用于信号处理和控制系统设计中。以下是一个使用C++实现多项式转换的简单案例。 假设我们有一个二次函数 f(x) = 2x^2 + 3x + 1,我们希望将其转换为一个三次函数 g(x) = 4x^3 + 3x^2 + 2x + 1。 首先,我们需要将 f(x) 转换为一个多项式系数表示的向量,即 [2, 3, 1]。然后,我们定义一个大小为 4 的数组 coeff,其中 coeff[i] 表示 g(x) 中 x^i 的系数。根据多项式转换的定义,我们可以得到以下方程组: coeff[0] = f(0) coeff[1] = f'(0) coeff[2] = f''(0) / 2 coeff[3] = f'''(0) / 6 其中 f'(x)、f''(x) 和 f'''(x) 分别表示 f(x) 的一阶导数、二阶导数和三阶导数。由于 f(x) 是一个二次函数,我们可以直接计算出这些导数: f'(x) = 4x + 3 f''(x) = 4 f'''(x) = 0 因此,我们可以得到: coeff[0] = f(0) = 1 coeff[1] = f'(0) = 3 coeff[2] = f''(0) / 2 = 2 coeff[3] = f'''(0) / 6 = 0 最终,我们得到了 g(x) 的系数向量 [1, 2, 3, 4]。下面是完整的 C++ 代码实现: ```c++ #include <iostream> #include <vector> using namespace std; vector<double> polyTransform(vector<double>& fCoeff, int n) { vector<double> gCoeff(n); gCoeff[0] = fCoeff[0]; gCoeff[1] = fCoeff[1]; gCoeff[2] = fCoeff[2] / 2; gCoeff[3] = fCoeff[3] / 6; return gCoeff; } int main() { vector<double> fCoeff = {2, 3, 1}; int n = 4; vector<double> gCoeff = polyTransform(fCoeff, n); for (int i = 0; i < n; i++) { cout << "g(x^" << i << ") = " << gCoeff[i] << endl; } return 0; } ``` 输出结果为: ``` g(x^0) = 1 g(x^1) = 2 g(x^2) = 3 g(x^3) = 4 ``` 即 g(x) = 4x^3 + 3x^2 + 2x + 1。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值