PHP - yüzer puan dizgeleştirir

7 Cevap php

Ben (tüm iyi bir neden için) 6 ve 8 arasında 10 rastgele yüzen üreten ve tefrika halinde bir mysql veritabanına bunları yazıyorum. Ama bir cilvesi depolama anda ortaya gibi görünüyor:

Saklamadan önce ben sadece neye benzediğini görmek için aynı veri çıktısı ediyorum ve bu benim olsun sonucudur

a:10:{i:0;d:6.20000000000000017763568394002504646778106689453125;i:1;d:7.5999999999999996447286321199499070644378662109375;i:2;d:6.4000000000000003552713678800500929355621337890625;..}

As you can see, I'm getting long numbers like 6.20000000000000017763568394002504646778106689453125 instead of what I'd really to like see, just 6.2. This is happening only when I serialize the data, if I just output the array, I do get the floats to one decimal. Here is my code:

function random_float ($min,$max) {
   return ($min+lcg_value()*(abs($max-$min)));
}

$a1 = random_float(6, 8);
$a1 = round($a1, 1);
$a2 = random_float(6, 8);
$a2 = round($a2, 1);	
$a3 = random_float(6, 8);
$a3 = round($a3, 1);
    ...
$array = array($a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8, $a9, $a10);

echo serialize($array);

7 Cevap

6.2 gibi bir sayı bunun hiçbir sonlu baz 2 temsili olarak orada tam bilgisayarlarda kayan noktalı matematik kullanılarak temsil edilemez. Ne echo numarayı-ing zaman görüyoruz insan okumasına yönelik bir şeydir, ve böylece değer yüzen 64 için doğruluk 32-bit ve 17 için (yaklaşık 6 ondalık yerler de ne sağlayabilir yuvarlanır -bit FP değerleri).

Bu değerleri seri hale getirilirken, ancak, gerçekten tam değerini (var olan yani tüm bit) ve sadece en yakın "güzel" bir değer istiyorum. Birden fazla şamandıra / yaklaşık 6.2 ve genellikle sizi serializing zaman really o kesin doğru bunları geri yüklemek için yaşıyorsanız son bit değerleri saklamak istediğiniz değerlendirir çift gösterimi var olabilir. Orada değerleri saçma "doğruluk" alıyoruz nedeni budur. Bu sadece başladı ne tam bit temsil korumak için hepsi bu.

Ama neden tam o sıkıca tefrika çıkışını kontrol etmek istiyorsun? Eğer veri yapısını yuvarlak açma ve daha sonra tekrar okumak böylece Yani, sadece orada. Kesinlikle bir yerde insanlar için çıktısında veya böylece tefrika gösterimini kullanmak istemiyorum. Sadece "güzel görünümlü" değerleri hakkında ise Yani, tamamen farklı bir amacı vardır serialize kullanmamalısınız.

İşte Gumbo cevabı almak benim. Bu foreach-mümkün olacaktır böylece ben orada IteratorAggregate koymak, ama aynı zamanda Sayılabilen ve ArrayAccess ekleyebilirsiniz.

<?php

class FloatStorage implements IteratorAggregate
{
  protected $factor;
  protected $store = array();

  public function __construct( array $data, $factor=10 )
  {
    $this->factor = $factor;
    $this->store = $data;
  }

  public function __sleep()
  {
    array_walk( $this->store, array( $this, 'toSerialized' ) );
    return array( 'factor', 'store' );
  }

  public function __wakeup()
  {
    array_walk( $this->store, array( $this, 'fromSerialized' ) );
  }

  protected function toSerialized( &$num )
  {
    $num *= $this->factor;
  }

  protected function fromSerialized( &$num )
  {
    $num /= $this->factor;
  }

  public function getIterator()
  {
    return new ArrayIterator( $this->store );
  }
}

function random_float ($min,$max) {
   return ($min+lcg_value()*(abs($max-$min)));
}

$original = array();
for ( $i = 0; $i < 10; $i++ )
{
  $original[] = round( random_float( 6, 8 ), 1 );
}

$stored = new FloatStorage( $original );

$serialized = serialize( $stored );
$unserialized = unserialize( $serialized );

echo '<pre>';
print_r( $original );
print_r( $serialized );
print_r( $unserialized );
echo '</pre>';

Benim için ben 3 yollar buldu:

  1. şamandıra var (örneğin 1.000.000 için) büyük bir sayıya çarpıldıktan sonra tamsayı float dönüştürmek; Eğer kullanılan zaman aynı 1000000 bölün unutmamalıyız gibi çok uygun bir yol değil
  2. kullanmak için preg_replace('/d:([0-9]+(\.[0-9]+)?([Ee][+-]?[0-9]+)?);/e', "'d:'.((float)$1).';'", $value); $ değeri float nerede; Bulunan here
  3. Ayrıca, round() tarafından şamandıra yuvarlak ve dize olarak dizide saklamak için.

Her durumda varyant # 2 kullanın

Casting also works, and it is faster, Example:

$a = 0.631;
$b = serialize($a);
$c = serialize((string)$a);
var_dump($b);

string (57) "d: 0,6310000000000000053290705182007513940334320068359375;"

var_dump($c);

string (12) "s: 5:" 0.631 ";"

var_dump(unserialize($b));

şamandıra (0.631)

var_dump(unserialize($c));

string (5) "0,631"

Önemli şey unserialize üzerine geri döküm için:

var_dump((float)unserialize($c));

şamandıra (0.631)

Sadece hassasiyet azaltmak:

ini_set('serialize_precision',2);

Kullandıktan sonra dizeleri olarak saklayın number_format:

$number = number_format($float, 2);

Tamsayılar (10 ile çarpılarak noktası önünde ilk ondalık yer kayması) ve bunu gerekirse onları geri dönüştürmek olarak saklayabilirsiniz:

function random_float($min,$max) {
    return ($min+lcg_value()*(abs($max-$min)));
}

$array = array();
for ($i=0; $i<10; $i++) {
    $array[] = (int) round(random_float(6, 8) * 10);
}
$serialized = serialize($array);
var_dump($serialize);

$array = unserialize($serialized);
foreach ($array as $key => $val) {
    $array[$key] = $val / 10;
}
var_dump($array);