Heim  >  Artikel  >  Backend-Entwicklung  >  PHP erreicht eine hochpräzise Berechnung

PHP erreicht eine hochpräzise Berechnung

小云云
小云云Original
2018-03-27 14:58:474613Durchsuche

In diesem Artikel erfahren Sie hauptsächlich, wie Sie in PHP hochpräzise Berechnungen durchführen können. Ich arbeite in der Finanzbranche und berechne häufig Gelder Hunderttausende Gelder verlieren, oder noch erschreckender. ..... Gehen wir direkt zum Beispiel:

Javascript

0,1 + 0,2 Warum ist es nicht gleich 0,3? Ergebnis: 0,30000000000000004)

0,8 * 7 Warum nicht gleich 5,6? (Richtiges Ergebnis: 5,6000000000000005)

PHP

var_dump(intval(0,58 * 100));

Das korrekte Ergebnis ist 57, nicht 58

Probleme durch Gleitkommaoperationen

Eigentlich sind diese Ergebnisse keine Fehler in der Sprache, sondern hängen mit dem Implementierungsprinzip des zusammen Alle Zahlen in js sind vereinheitlicht, einschließlich Ganzzahlen. Tatsächlich sind alle Zahlen vom Typ double.

Und PHP unterscheidet zwischen int und float. Unabhängig von der Sprache gibt es ähnliche Probleme, solange es sich um Gleitkommaoperationen handelt. Sie müssen daher bei der Verwendung darauf achten.

Hinweis: Wenn Sie PHPs +-*/ zur Berechnung von Gleitkommazahlen verwenden, kann es zu Problemen mit falschen Berechnungsergebnissen kommen. Beispielsweise gibt das obige echo intval(0.58*100); 57 aus 58. Dies ist tatsächlich ein Fehler, da das zugrunde liegende Binärsystem des Computers Gleitkommazahlen nicht genau darstellen kann. Dieses Problem ist auch bei der Verwendung von Python aufgetreten. Grundsätzlich bieten die meisten Sprachen Klassenbibliotheken oder Funktionsbibliotheken für präzise Berechnungen. PHP verfügt beispielsweise über eine hochpräzise BC-Funktionsbibliothek. Später werde ich einige häufig verwendete hochpräzise BC-Funktionen vorstellen.

Kehren wir zu den Fragen 57 und 58 oben zurück.

Warum ist die Ausgabe 57? Handelt es sich um einen PHP-Fehler?

Um diesen Grund zu verstehen, müssen wir zunächst die Darstellung von Gleitkommazahlen (IEEE 754) kennen:

Gleitkommazahlen werden am Beispiel einer Länge von 64 Bit (doppelte Genauigkeit) durch 1 Vorzeichenbit (E), 11 Exponentenbits (Q) und 52 Mantissenbits (M) (insgesamt 64 Bits) dargestellt. .

Vorzeichenbit: Das höchste Bit gibt das Vorzeichen der Daten an, 0 gibt eine positive Zahl an und 1 gibt eine negative Zahl an.

Exponentenbit: Stellt die Potenz der Daten zur Basis 2 dar, und der Exponent wird durch einen Offset-Code dargestellt

Mantisse: Stellt die signifikanten Stellen nach dem Dezimalpunkt der Daten dar

Die wichtigsten Punkte hier sind die Darstellung von Dezimalzahlen im Binärformat. Ich werde hier nicht näher darauf eingehen Binärdarstellung, 0,58 ist ein unendlicher Wert (die Zahlen unten ohne die implizite 1). 100011110 1011100001010001111010111000010100011110 Und die Binärdatei von beiden, wenn sie nur bestehen Für Berechnungen dieser 52 Bits sind sie: www.111cn.net

 0,58 -> 0,579999999999 Wir betrachten es nicht im Detail, wenn Sie interessiert sind. Sie können sehen (Gleitkomma), schauen wir es uns mit Kopfrechnen vage an ... 0,58 * 100 = 57,999999999

Wenn Sie es dann intvalieren, wird es das tun natürlich 57...

Es ist ersichtlich, dass der Schlüssel zu diesem Problem der Punkt ist: „Ihre scheinbar endliche Dezimalzahl ist in der Binärdarstellung des Computers tatsächlich unendlich“

Daher Glaube nicht mehr, dass das ein PHP-Bug ist, das ist es...

Der PHP-Gleitkommatyp hat eine Ungenauigkeit von +-* %/

Schauen Sie sich weiterhin ein Stück davon an Code:

Der gedruckte Wert ist boolean false
$a = 0.1;
$b = 0.7;
var_dump(($a + $b) == 0.8); // false

Warum ist das so? Das PHP-Handbuch enthält die folgende Warnmeldung für Gleitkommazahlen:

Warnung

Gleitkomma-Präzision

  显然简单的十进制分数如同 0.1 或 0.7 不能在不丢失一点点精度的情况下转换为内部二进制的格式。这就会造成混乱的结果:例如,floor((0.1+0.7)*10) 通常会返回 7 而不是预期中的 8,因为该结果内部的表示其实是类似 7.9999999999…。

  这和一个事实有关,那就是不可能精确的用有限位数表达某些十进制分数。例如,十进制的 1/3 变成了 0.3333333. . .。

 所以永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。如果确实需要更高的精度,应该使用任意精度数学函数或者 gmp 函数

那么上面的算式我们应该改写为

$a = 0.1;
$b = 0.7;
var_dump(bcadd($a,$b,2) == 0.8); // true

常用的高精度函数如下:

bc是Binary Calculator的缩写

bcadd — 将两个高精度数字相加
  bccomp — 比较两个高精度数字,返回-1, 0, 1
  bcp — 将两个高精度数字相除
  bcmod — 求高精度数字余数
  bcmul — 将两个高精度数字相乘
  bcpow — 求高精度数字乘方
  bcpowmod — 求高精度数字乘方求模,数论里非常常用
  bcscale — 配置默认小数点位数,相当于就是Linux bc中的”scale=”
  bcsqrt — 求高精度数字平方根
  bcsub — 将两个高精度数字相减
BC高精确度函数库包含了:相加,比较,相除,相减,求余,相乘,n次方,配置默认小数点数目,求平方。这些函数在涉及到有关金钱计算时比较有用,比如电商的价格计算。
/**
  * 两个高精度数比较
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return int $left==$right 返回 0 | $left<$right 返回 -1 | $left>$right 返回 1
  */
var_dump(bccomp($left=4.45, $right=5.54, 2));
// -1
  
 /**
  * 两个高精度数相加
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return string 
  */
var_dump(bcadd($left=1.0321456, $right=0.0243456, 2));
//1.05
 
  /**
  * 两个高精度数相减
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return string 
  */
var_dump(bcsub($left=1.0321456, $right=3.0123456, 2));
//-1.98
  
 /**
  * 两个高精度数相除
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return string 
  */
var_dump(bcp($left=6, $right=5, 2));
//1.20
 
 /**
  * 两个高精度数相乘
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return string 
  */
var_dump(bcmul($left=3.1415926, $right=2.4569874566, 2));
//7.71
 
 /**
  * 设置bc函数的小数点位数
  * 
  * @access global
  * @param int $scale 精确到的小数点位数
  * 
  * @return void 
  */ 
bcscale(3);
var_dump(bcp(&#39;105&#39;, &#39;6.55957&#39;)); 
//php7.1 16

所以平时程序要要封装方法:

/**
 * 精确加法
 * @param [type] $a [description]
 * @param [type] $b [description]
 */
function math_add($a,$b,$scale = &#39;2&#39;) {
  return bcadd($a,$b,$scale);
}
/**
 * 精确减法
 * @param [type] $a [description]
 * @param [type] $b [description]
 */
function math_sub($a,$b,$scale = &#39;2&#39;) {
  return bcsub($a,$b,$scale);
}
/**
 * 精确乘法
 * @param [type] $a [description]
 * @param [type] $b [description]
 */
function math_mul($a,$b,$scale = &#39;2&#39;) {
  return bcmul($a,$b,$scale);
}
/**
 * 精确除法
 * @param [type] $a [description]
 * @param [type] $b [description]
 */
function math_p($a,$b,$scale = &#39;2&#39;) {
  return bcp($a,$b,$scale);
}
/**
 * 精确求余/取模
 * @param [type] $a [description]
 * @param [type] $b [description]
 */
function math_mod($a,$b) {
  return bcmod($a,$b);
}
/**
 * 比较大小
 * @param [type] $a [description]
 * @param [type] $b [description]
 * 大于 返回 1 等于返回 0 小于返回 -1
 */
function math_comp($a,$b,$scale = &#39;5&#39;) {
  return bccomp($a,$b,$scale); // 比较到小数点位数
}

相关推荐:

php如何处理高精度计算函数

Das obige ist der detaillierte Inhalt vonPHP erreicht eine hochpräzise Berechnung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn