2014-02-20 9 views
9

gleichmässig teilbar ist Ich möchte einfach wissen, ob $ x durch $ y teilbar ist. Zum Beispiel willen annehmen:

$x = 70; 
$y = .1; 

Das erste, was ich versuchte, ist:

$x % $y 

Dies scheint zu funktionieren, wenn beide Zahlen ganze Zahlen sind aber nicht, wenn sie nicht und wenn $y sind, ist eine Dezimalzahl kleiner als 1 zurückkehrt eine „Division durch Null“ Fehler, so ist, dann habe ich versucht:

fmod($x,$y) 

Welche ebenso verwirrend Ergebnisse zurückgibt, „,099999999999996“.

php.net heißt fmod():

Gibt den Gleitkomma- Rest der Dividende des Teilens (x) durch den Divisor (y)

Well nach meinem Rechner 70/.1 = 700. Was bedeutet, dass der Rest 0 ist. Kann jemand bitte erklären, was ich falsch mache?

+0

Nicht alle Werte eine Darstellung genaue Float-Punkt: es ist also nicht „[perfekt] gleichmäßig“ (außer im Fall von ganzen Zahlen) so viel wie "nahe genug". – user2864740

+0

[Das passiert auch mit bcmod().] (Http://codepad.viper-7.com/ztb1Y2) –

Antwort

10

Eine Lösung wäre eine normale Division und dann den Wert mit der nächsten Ganzzahl zu vergleichen. Ist das Ergebnis, daß ganze Zahl ist oder sehr nahe zu dieser ganzen Zahl ist das Ergebnis geradzahlig teilbar:

$x = 70; 
$y = .1; 

$evenlyDivisable = abs(($x/$y) - round($x/$y, 0)) < 0.0001; 

Diese beiden Zahlen subtrahieren und überprüft, dass die absolute Differenz kleiner als ein bestimmte Rundungsfehler. Dies ist die übliche Art und Weise Gleitkommazahlen zu vergleichen, da je nachdem, wie Sie die Darstellung eines Schwimmers bekam variieren kann:

php> 0.1 + 0.1 + 0.1 == 0.3 
bool(false) 
php> serialize(.3) 
'd:0.29999999999999999;' 
php> serialize(0.1 + 0.1 + 0.1) 
'd:0.30000000000000004;' 

diese Demo Siehe:

php> $x = 10; 
int(10) 
php> $y = .1; 
double(0.1) 
php> abs(($x/$y) - round($x/$y, 0)) < 0.0001; 
bool(true) 
php> $y = .15; 
double(0.15) 
php> abs(($x/$y) - round($x/$y, 0)) < 0.0001; 
bool(false) 
+0

Ich bin an dieser Lösung interessiert. Eine Frage, warum Subtraktion und '<0,0001' statt einfach' ($ x/$ y) == round ($ x/$ y) '? – billynoah

+0

@billynoah Ich änderte meine Antwort und erklärte, warum Floats so verglichen werden sollten. – TimWolla

+0

Danke Tim, ich denke ich verstehe, aber in welchem ​​Szenario würde die Methode, die ich in dem obigen Kommentar vorgeschlagen habe, ein anderes Ergebnis liefern als deines? – billynoah

6

.1 nicht habe eine genaue Darstellung im binären Fließkomma, was zu einem falschen Ergebnis führt. Sie könnten sie mit einer ausreichend großen Potenz von 10 multiplizieren, so dass sie ganze Zahlen sind, dann% verwenden und dann zurück konvertieren. Dies beruht darauf, dass sie sich nicht durch einen Faktor unterscheiden, der groß genug ist, dass ein Multiplizieren mit der Potenz von 10 dazu führt, dass einer von ihnen überläuft/die Genauigkeit verliert. Wie folgt:

$x = 70; 
$y = .1; 
$factor = 1.0; 
while($y*$factor != (int)($y*$factor)){$factor*=10;} 
echo ($x*$factor), "\n"; 
echo ($y*$factor), "\n"; 
echo (double)(($x*$factor) % ($y*$factor))/$factor; 
+0

Die Zahlen basieren auf Benutzereingaben in einem Formular. Sie könnten alles numerische sein. – billynoah

+0

Ich habe es so festgelegt, dass die einzige int-basierte Größenbeschränkung die endgültige Größe von y ist und nicht die Differenz zwischen x und y. Der zulässige Unterschied ist nur durch die Begrenzungen von Schwimmern begrenzt. – Tyler

+1

+1 für ".1 hat keine exakte Darstellung in binären Fließkommawerten" (Siehe den Patriot Bug: http://sydney.edu.au/engineering/it/~alum/patriot_bug.html) – dognose

0

Float-Point-Darstellung variiert von Maschine zu Maschine. Zum Glück gibt es Standards. PHP verwendet typischerweise das IEEE 754-Format mit doppelter Genauigkeit für die Gleitkommadarstellung, die einer der gebräuchlichsten Standards ist. See here für weitere Informationen dazu. Werfen Sie einen Blick auf this calculator für ein besseres Verständnis in Bezug auf die warum. Wie für die wie Ich mag Tims Lösung, vor allem, wenn Sie mit Benutzereingaben zu tun haben.

4

Es ist eine reine Mathematik-Bibliothek in bitbucket: https://bitbucket.org/zdenekdrahos/bn-php

Die Lösung wird dann sein:

php > require_once 'bn-php/autoload.php'; 
php > $eval = new \BN\Expression\ExpressionEvaluator(); 
php > $operators = new \BN\Expression\OperatorsFactory(); 
php > $eval->setOperators($operators->getOperators(array('%'))); 
php > echo $eval->evaluate('70 % 0.1'); // 0 
0.00000000000000000000 

getestet auf PHP5.3

Credits: http://www.php.net/manual/en/function.bcmod.php#111276

0

Wie Sie sagten, die Verwendung des Modulo-Operators funktioniert gut, wenn es sich um eine Ganzzahl handelt. Warum also nicht einstellen? up, so dass es auf ganzen Zahlen arbeitet. In meinem Fall musste ich Teilbarkeit von 0,25 zu überprüfen:

$input = 5.251 
$x = round($input, 3); // round in case $input had more decimal places 
$y = .25; 
$result = ($x * 1000) % ($y * 1000); 

In Ihrem Fall:

haben
$input = 70.12 
$x = round($input, 2); 
$y = .1; 
$result = ($x * 100) % ($y * 100);