1.23 这个数值,在 float 在内部二进制转换时已经失真,为什么乘以 100 之后,最终的高精度表示结果 不是 1.2299999**** 类似这样的,而是 1.23000000000 这个结果呢?
PHP 的乘法运算符进行了猜测修正吗?
为什么乘以 100,最终结果是保真的, 乘以 10,不是保真的呢?
乘法运算符的代码: https://github.com/php/php-src/blob/master/Zend/zend_multiply.h
求大神解读。
[运行结果在 PHP5.4、5.6 和 7.1 上无差异,另外, 乘以 10 的结果是不保真的]
运行代码为:
<?php
echo "<meta charset='utf-8'>";
$a = 1.23;
$b = 100; //第二次运行 $b = 10;
echo '
<pre>
$a = 1.23;
$b = 100;
</pre><hr>
';
echo '$a 的高精度数值为:';
printf("%0.30f", $a);
echo '<hr>';
echo '$b 的高精度数值为:';
printf("%0.30f", $b);
echo "<br><br><hr>";
echo '$a 的数据类型: ';
var_dump($a);echo "<hr>";
echo '$b 的数据类型: ';
var_dump($b);echo "<hr>";
$total = $a * $b;
echo '执行的计算为:
<pre>
$total = $a * $b;
</pre>
';
echo "<br><br><hr>";
echo '计算结果:$total 为 ';
var_dump($total);echo "<hr>";
echo '高精度计算结果: ';
printf("%0.99f", $total);
1
ovear 2017-10-06 12:10:27 +08:00
http://www.cnblogs.com/FlyingBread/archive/2009/02/15/660206.html
5.2 浮点数的乘除法 ( 1 )阶码运算:阶码求和(乘法)或阶码求差(除法) 即 [Ex+Ey]移= [Ex]移+ [Ey]补 [Ex-Ey]移= [Ex]移+ [-Ey]补 ( 2 )浮点数的尾数处理:浮点数中尾数乘除法运算结果要进行舍入处理 例题:X=0 .0110011*211,Y=0.1101101*2-10 求 X*Y 解:[X]浮:0 1 010 1100110 [Y]浮:0 0 110 1101101 ( 1 )阶码相加 [Ex+Ey]移=[Ex]移+[Ey]补=1 010+1 110=1 000 1 000 为移码表示的 0 ( 2 )原码尾数相乘的结果为: 0 10101101101110 ( 3 )规格化处理:已满足规格化要求,不需左规,尾数不变,阶码不变。 ( 4 )舍入处理:按舍入规则,加 1 进行修正 所以 X ※ Y= 0.1010111*20 |
2
xiaoyanbot OP |
3
ovear 2017-10-06 12:22:33 +08:00
@xiaoyanbot 这个跟 PHP 没有关系,是计算方法产生的误差。
http://218.5.241.24:8018/C35/Course/ZCYL-HB/WLKJ/jy/Chap02/2.7.2.htm 详细可以看这里 很显然 LZ 对高精度有一定误解,注意一下 PHP 官网的这段话 http://php.net/manual/zh/language.types.float.php 浮点数的字长和平台相关,尽管通常最大值是 1.8e308 并具有* 14 位十进制数字的精度*( 64 位 IEEE 格式)。 ``` <?php $a = 1.23; var_dump($a); printf('%.13f', $a); ``` lz 执行下这个就知道了 |
4
xiaoyanbot OP @ovear
谢谢解答。 精度丢失这个问题我知道, 我的代码里也有如上您给出的代码的表示。 我的疑问是: 按照 [尾数修正规则] 情况下, 1.23 * 100 这个操作,正好恰巧修正成了 123.000000000**** 这个结果,对吗? |
5
xiaoyanbot OP ~~~
(2) 尾数处理 浮点加减法对结果的规格化及舍入处理也适用于浮点乘除法。 第一种简单的办法是,无条件地丢掉正常尾数最低位之后的全部数值。这种办法被称为截断处理,其好处是处理简单,缺点是影响结果的精度。 第二种简单办法是,运算过程中保留中移出的若干高位的值,最后再按某种规则用这些位上的值修正尾数,这种处理方法被称为舍入处理。 当尾数用原码表示时,舍入规则比较简单。最简便的方法,是只要尾数最低位为 1,或移出的几位中有为 1 的数值位,就使最低位的值为 1。另一种是 0 舍 1 入法,即当丢失的最高位的值为 1 时,把这个 1 加到最低数值位上进行修正,否则舍去丢失的各位的值。这样处理时,舍入效果对负数是相同的,入将使数的绝对值变大,舍则使数的绝对值变小。 当尾数是用补码表示时,所用的舍入规则,应该与用原码表示时产生相同的处理效果,具体规则是: 当丢失的各位均为 0 时,不必舍入; 当丢失的最高位为 0,以下各位不全为 0 时,或者丢失的最高位为 1,以下各位均为 0 时,则舍去丢失位上的值; 当丢失的最高位为 1,以下各位不全为 0 时,则执行在尾数最低位入 1 的修正操作。 ~~~ |
6
ovear 2017-10-06 12:58:28 +08:00
@xiaoyanbot 首先,浮点数超出了精度范围的数字,是被认为无效的,或者说没有意义的。
所以在有效范围内 1.23 ( 14 位有效位数) 和 1.2999999999999 ( 14 位有效位数) 的 IEE 754 表示是不同的,他们不是一个数字。 所以这样的结果再加上上面的计算方法,计算结果是能确保修正到 [预期值] 的。 |
7
xiaoyanbot OP |
8
ovear 2017-10-06 13:30:54 +08:00
@xiaoyanbot 没有强制规定
|
9
azh7138m 2017-10-06 14:40:02 +08:00 via Android
@xiaoyanbot 截断和舍入指的是 printf 这种函数在把它转换成字符表示时的处理方式
|