PHP kriegt das mit den Nummern nicht gebacken

Da las ich vor einigen Tagen einen interessanten Beitrag über die haarsträubenden Schwächen von PHP — und erfuhr eines der genannten Problem heute am eigenen Leibe.

Eine meiner Funktionen skaliert Bilder, um sie danach aus der Mitte heraus zuzuschneiden. Vor dem Zuschnitt wird das Bild skaliert. Ziel ist es dabei, das Bild so zu skalieren, dass sowohl die Höhe und Breite des Zielformats überschritten werden, denn so lässt sich mit gutem Gewissen ein Zuschnitt vornehmen.

Mein Code sieht folgendermassen aus:

...
for($i = 0; $i<=1; $i++) {
$factor = $dimDestination[$i]/$dimOriginal[$i];

$dimTests[$i] = array($dimOriginal[0]*$factor, $dimOriginal[1]*$factor, $factor);
}

$factor = null;

foreach($dimTests as $key=>$dimTest) {
if($dimTest[0] >= $dimDestination[0] && $dimTest[1] >= $dimDestination[1]) {
$factor = $dimTest[2];
$keySelected = $key;
}
else {
$this->debug->add($dimTest[0] . '<' . $dimDestination[0] . ', ' . $dimTest[1] . '<' . $dimDestination[1]);
}
}

if($factor === null) {
$this->debug->add('$factor ' . $factor . ' is null');
return false;
}

Bei einem speziell zugeschnittenen Bild wurde $factor immer auf null gesetzt und die Funktion brach mit false ab — obwohl das Bild eigentlich problemlos skaliert und zugeschnitten hätte werden sollen.

Nach einer halbstündigen Fehlersuche kam ich den Problem schlussendlich auf die Spur. Zuerst einmal schaute ich mir die in $this->debug gespeicherten Infos an:

emeidiImage->cropFromCenter()
505<505, 131.510416667<66

Was zum Teufel?! Wieso ist 505 kleiner als 505? Mit vardump() lichtete sich der Nebel über dem falschen Verhalten:

array(2) {
[0]=>
array(3) {
[0]=>
float(505)
[1]=>
float(131.510416667)
[2]=>
float(1.05208333333)
}
...
}

array(2) {
[0]=>
int(505)
[1]=>
int(66)
}

Ich verglich float(505) mit int(505), und da lag wohl die Krux vergraben! Nun, dachte ich mir, wandeln wir die Dimension halt bei der Multiplikation der Ursprungsdimension mit dem Faktor in einen Integer-Wert um:

$dimTests[$i] = array((int)($dimOriginal[0]*$factor), (int)($dimOriginal[1]*$factor), $factor);

Weiterhin brach die Funktion ab. Was zum Teufel … ein erneuter Blick auf vardump() zeigte mir endlich die wirklich Ursache des Problems:

array(2) {
[0]=>
array(3) {
[0]=>
int(504)
[1]=>
int(131)
[2]=>
float(1.05208333333)
}
...
}
array(2) {
[0]=>
int(505)
[1]=>
int(66)
}

Ahaaa! Irgendwie war der Fliesskommawert eben nicht ganz genau 505, sondern vielleicht nur 504.999999.

Nach folgender Anpassung unter Zuhilfenahme der Funktion ceil() lief die Funktion endlich fehlerfrei durch:

$dimTests[$i] = array(ceil($dimOriginal[0]*$factor), ceil($dimOriginal[1]*$factor), $factor);

Tjach. Heute habe ich deshalb gelernt, dass int(505) noch lange nicht float(505) ist.

Nachtrag

Das Problem tritt mit der bei cyon.ch installierten PHP-Version 5.2.17 auf, nicht aber mit der auf meinem Entwicklungsserver installierten PHP-Version 5.4.0-3 auf.