Kirjautuminen

Haku

Tehtävät

Keskustelu: Nettisivujen teko: Muunnos kokonaisluvuksi -bugi?!

Sivun loppuun

T.M. [14.10.2005 12:04:07]

#

Huomasin vähän aikaa sitten, että (int)$luku muunnosta käytettäessä syntyy välillä virhe:
http://koti.mbnet.fi/winuus/jakotesti.php?luku=24

Alunperin luulin että koodissani oli virhe, kun lukujen summaaminen yhteen ei tuottanutkaan alkuperäistä lukua, mutta nähtävästi kyseessä olikin PHP:n sisäinen bugi(!?)

Ongelman saa korjattua (int)(string)$luku purkalla, mutta onko kellään parempaa ehdotusta?
Edit: Lukujen pyöristys olisi eräs keino, mutta tällöin tulos vääristyy paikoittain.


Yllä mainitun PHP-scriptin lähdekoodi:

<?php

function scan_luku($luku, $pituus = 100){
	$ulos = array();
	for($u = 1; $u <= $pituus; $u++){
		$jako = $u;
		$k = $luku/$jako;
		$yht = $k;
		for($s = 0; $s < $jako; $s++){
			if($s == $jako-1){
				if((int)$yht != $luku){
					$ulos[] = $u;
				}
			}
			$yht += $k;
		}
	}
	return $ulos;
}



$luku = $_GET['luku'];


$mm_luku = 500;
$mm_skaala = 500;

$lista = scan_luku($mm_luku, $mm_skaala);
$max = count($lista);

if($luku){
	print"(float) on alkuperäinen luku, jolle ei ole tehty mitään muunnoksia. (int) on intiksi muunnettu luku.<br><br>";
	$jako = $luku;
	$k = $mm_luku/$jako;
	$yht = $k;
	for($s = 0; $s < $jako; $s++){
		if((int)$yht != (int)(string)$yht){
			print"<b>(float)$yht = (int)".(int)$yht."</b> (pitäis tulla $yht, eikä ".(int)$yht."!!)";
		}else{
			print"(float)$yht = (int)".(int)$yht;
		}

		print"<br>";

		$yht += $k;
	}
	print"<br><br><hr><br><br>";
}


print"Luku $mm_luku jaettuna 1-$mm_skaala luvuilla, löytyy yhteensä $max virheellistä int-muunnoksen tulosta:<br><BR>";
foreach($lista as $key => $val){
	print"<A HREF=\"?luku=$val\">$val</A>, ";
}

?>

kayttaja-2791 [14.10.2005 13:03:49]

#

Noissa muunnoksissa muistaakseni oli jotain bugeja. Oletko kokeillut intval()-funktiota? Toki voisit tehdä oman muunnosfunktionkin...

Lebe80 [14.10.2005 14:38:21]

#

No siis varsinaista virhettähään ei php:n (int) ja (float):ssa ole, vaan suurin virhe näyttäisi olevan koodin rakenteessa.
Koska tietokoneet käsittelevät lukuja N desimaalin tarkkuudella, olisi suositeltavaa tehdä ensin kertolaskut jonka jälkeen vasta jakolaskut.

Muutenkin olet saanut yksinkertaisen (X*Y)/Z (jossa 1<=Y<=Z)laskun näyttämään erittäin epäselvältä, ja käyttämällä useita muuttujia (joka selittäisi pyöristysvirheet).

<?php
$luku = (intval($_GET['luku'])+1)-1;
$mm_luku = 500;
$mm_skaala = 500;
if ($luku>0){
	for ($i=1;$i<=$luku;$i++){
		$lukulasku = ($mm_luku*$i)/$luku;
		echo "(float)$lukulasku ... (int)".(int)$lukulasku."";
		if ( (int)(string)$lukulasku != (int)$lukulasku){
			echo " --->  Virhe(float)$lukulasku != (int)".(int)$lukulasku."";
		}
		echo "<br>";
	}
}
?>

T.M. [14.10.2005 14:55:32]

#

Lebe80 kirjoitti:

Muutenkin olet saanut yksinkertaisen (X*Y)/Z (jossa 1<=Y<=Z)laskun näyttämään erittäin epäselvältä, ja käyttämällä useita muuttujia (joka selittäisi pyöristysvirheet).

Virhe ei edelleenkään ole laskennallinen, sillä tulostettaessa muuttujaa se näyttää ilman int-muunnosta tasan 500, mutta muunnoksen jälkeen se onkin 499 jostain kumman syystä:

print $luku; // 500
print (int)$luku; // 499
print floor($luku); // 499
print intval($luku); // 499

Edit: Vastaus alla olevaan: PHP:n version on 4.3.10

Lebe80 [14.10.2005 14:56:21]

#

Mikä php-versio, sillä itselläni ei tätä ongelmaa ole?

EDIT:
otetaas takasin, eli itselläni tulee näistä samat tulokset seuraavalla arvolla.

$luku = 499.9999999999999;
print $luku; // 500
print (int)$luku; // 499
print floor($luku); // 499
print intval($luku); // 499

kun taas
$luku = 499.99999999999999; // yksi 9 lisää
print $luku; // 500
print (int)$luku; // 500
print floor($luku); // 500
print intval($luku); // 500

T.M. [14.10.2005 15:21:50]

#

Eli PHP ei tulosta lukua sellaisenaan kuin se on muistissa, vaan pyöristää sen ylöspäin. int-muunnos taas käyttää pyöristämätöntä lukua, jolloin syntyy virhe...

Onko mitään ehdotusta ongelman korjaamiseen?
Pienen luvun lisääminen lukuun ei ole toimiva, koska luvun tarkkuus ei ole aina sama (eli jos tekisi tyyliin: (int)($luku+0.000000001)).

Lebe80 [14.10.2005 15:42:51]

#

No ainakin tietty laskujärjestys ei pyöristä lukuja niin paljoa väärin.

Samoin "turhien" välivaiheiden vähentäminen minimoi virhettä.


===
edit:
sooda: et ole yhtään mukana.

sooda [14.10.2005 15:43:30]

#

Yleisesti tunnettu koodi kokonaisluvuksi pyöristämiseenhä on (int)(luku + 0.5). (positiivisille. negatiivisilla + -> -)

tsuriga [14.10.2005 16:19:59]

#

(int)((int)$luku) :?

Lebe80 [14.10.2005 16:42:40]

#

tsuriga: eipä auta kokonaisluvun kokonaisluvuksi muuttaminen....

Itse näkisin ainoan ratkaisun suunnittelemalla laskutoimitukset toisella tavalla, koska Php ei näemmä pysty sisältämään 14 desimaalia.

tsuriga [16.10.2005 17:12:33]

#

Hups siis ceil((int)$luku). Toisaalta jos luku näytetään nyt merkkijonona "500" ja sitä käsitelläänkin 500.0000000001:nä niin tulee taas ongelmia, lieneekö moista ongelmaa?

T.M. [21.10.2005 22:02:28]

#

Häh? ceil() pyöristää 50.999 luvun ylöspäin, ja int pudottaa desimaalit, eli tuosta ceil((int)$luku) tuottaisi 50, koska se yrittää pyöristää lukua 50 ylöspäin, mutta eihän siinä ole desimaaleja enää olemassakaan, joten se ei siitä miksikään muutu.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta