12
votes

PHP - Serialize Points flottants

Je crée 10 flotteurs aléatoires entre 6 et 8 (toutes pour une bonne raison) et les écrire dans une base de données MySQL sous une forme sérialisée. Mais une bizarrerie semble émerger à l'heure de stockage:

Avant de stocker Je ne fais que sortir les mêmes données pour voir à quoi cela ressemble, et c'est le résultat que je reçois p> xxx pré>

Comme vous pouvez le voir, je reçois des nombres longs comme 6.20000000000000017763568394002504646778106689453125 Au lieu de ce que je voudrais vraiment voir, juste 6.2. Cela ne se produit que lorsque je serai sérialisé les données, si je viens de sortir le tableau, je reçois les flotteurs à une décimale. Voici mon code: P>

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);


1 commentaires

Ressemble à des rondes d'écho flotte, mais c'est étrange


9 Réponses :


3
votes

Rangez-les comme entiers (déplacez la première place décimale devant le point en le multipliant par 10) et convertissez-les si vous en avez besoin:

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);


1 commentaires

C'est vraiment une bonne solution, mais je pense que je vais rester avec la sérialisation pour le moment, pour des raisons que j'ai écrites dans le commentaire ci-dessus. Merci quand même pour vôtre aide



16
votes

Un chiffre comme 6.2 ne peut pas être représenté avec exactement en utilisant des mathématiques à virgule flottante dans des ordinateurs car il n'y a pas de représentation finie de base-2 de celui-ci. Ce que vous voyez lorsque echo - -ing, le numéro est destiné à la lecture humaine, et la valeur sera donc arrondie à ce que les flotteurs peuvent fournir une précision (environ 6 décimales pour 32 bits et 17 pour Valeurs FP 64 bits).

Lorsque vous sérialisez ces valeurs, vous voulez vraiment la valeur exacte (i. E. Tous les bits qui sont là) et non seulement la "belle" la plus proche. Il pourrait y avoir plus d'un flotteur / double représentation qui évalue à environ 6,2 et lorsqu'il est généralement sérialisé vraiment vouloir stocker des valeurs exactes au dernier bit que vous avez pour les restaurer correctement. C'est pourquoi vous obtenez une "précision" ridicule en valeurs là-bas. Tout simplement pour préserver la représentation exacte des bits de ce que vous avez commencé.

Mais pourquoi voulez-vous contrôler la sortie sérialisée qui fermement? Je veux dire, c'est juste là pour que vous puissiez aller de retour à votre structure de données et de le lire plus tard. Vous ne voulez certainement pas utiliser cette représentation sérialisée quelque part dans la sortie des humains. Donc, si c'est à peu près des valeurs «belles recherches», vous ne devriez pas utiliser Serialize qui a un objectif totalement différent.


5 commentaires

Ah, merci pour l'explication, ça va probablement aller très loin. La raison pour laquelle je voudrais garder mon tableau sérialisé simple est dû à la peur qu'une chaîne de série plus longue augmente globalement la charge sur le serveur de requêtes. J'aimerais apprendre que je me trompe. Merci à tous les autres pour les réponses rapides ... vous gars rock! Juste pour garder les choses simples mais (c.-à-d. Pas de fonctions supplémentaires), je risque de rester avec cette solution, bien que Gumbo le vôtre soit très élégant.


Eh bien, des bases de données sont à peu optimisées pour le cas d'utilisation de la récupération de données. Que vous lisiez 200 octets ou 2000 ne devriez pas faire une grande partie de la différence que si vous faites quelque chose de vraiment intense. Mais vous pouvez toujours optimiser quand cela se révèle être un goulot d'étranglement.


J'ai rencontré cette question lors du stockage des données sérialisées. Je stockais un grand nombre d'enregistrements afin d'utiliser littéralement 58 chiffres pour stocker ce qui aurait dû être 4 caractères semblait simplement inacceptable. J'ai utilisé la solution numer_format fournie par Shadowhand ci-dessous.


J'ai résolu ceci en définissant la précision et Serialize_Precision sur la même valeur (10): INI_SET ('Precision', 10); ini_set ("Serialize_Precision ', 10); Vous pouvez également définir ceci dans votre php.ini


Une explication sans réponse est comme si les hommes d'incendie se présentent dans un incendie, puis rejoignent les spectateurs.



1
votes

Voici ma réponse de Gumbo. Je mets itératoragregistrés là-bas afin que ce soit pourvu, mais vous pourriez ajouter de manière comptable et arrayAccess aussi.

<?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>';


0 commentaires

8
votes

Stockez-les comme chaînes après avoir utilisé Number_Format :

$number = number_format($float, 2);


1 commentaires

Number_Format n'est pas un moyen sûr de convertir des nombres en chaîne. L'EN_US LOCALE ajoutera des virgules groupées. "1234.56" devient "1,234.56". Ajouter une chaîne vide au numéro à convertir. Numéro de $. = '';



1
votes

Pour moi, j'ai trouvé 3 façons:

  1. convertir le flotteur en entier après que Float var est multiplié à un grand nombre (par exemple, 1 000 000); Ce n'est pas un moyen très pratique que vous ne devriez pas oublier de diviser par le même 1 000 000, quand il est utilisé
  2. utiliser preg_replace ('/ d: ([0-9] + (\. [0-9] +)? ([EE] [+ -]? [0-9] +)?) ; / e ', "' d: '. (((flottant) 1 $).'; '" "Valeur $); où $ valeur est votre flotteur; trouvé ici
  3. Aussi, pour arrondir le flotteur par rond () et stocker Il est en matrice en tant que chaîne.

    Dans tous les cas, j'utilise la variante n ° 2


0 commentaires

2
votes

casting fort> aussi fonctionne strong>, et c'est plus rapide fort>, Exemple: xxx pré>

chaîne (57) "D: 0.63100000000000000005329070518200751394033432907059375;" P>

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


0 commentaires

6
votes

Il suffit de réduire la précision:

ini_set('serialize_precision',2);


1 commentaires

Cela devrait être une réponse correcte, car c'est la bonne solution, pas une solution de contournement.



0
votes

PHP.INI FICHIER contient un Serialize_Precision La directive, qui vous permet de contrôler combien de chiffres significatifs seront sérialisés pour votre flotteur. Dans votre cas, stocker une seule décimale de nombres entre 6 et 8 signifie deux chiffres significatifs.

Vous pouvez définir ce paramètre dans le fichier php.ini ou directement dans votre script: xxx

si vous ne vous souciez pas du nombre exact de chiffres significatifs, mais de vous soucier de Ne pas avoir de spaghetti de chiffres résultant de la manière dont les numéros de flotteur sont stockés, vous pouvez également donner une valeur de -1, qui invoque un "algorithme d'arrondi spécial", cela est susceptible de faire exactement ce qui est requis: xxx

Vous pouvez même le réinitialiser à sa valeur d'origine après votre sérialisation: xxx


0 commentaires

0
votes

Définition de votre valeur Serialize_Precision dans php.ini à -1 résoudra le problème de point flottant, ou vous pouvez la définir sur une valeur que vous préférez, conformément aux spécifications: http://php.net/manual/fr/ini. core.php # ini.Serialize-précision

versions PHP <= 5.3.5 expédiés avec la valeur par défaut de "100", tandis que la valeur par défaut à la version 7.0.33 est "17", bien que le paquet emballé avec votre distribution ait pu être expédié avec un "-1".

Comme indiqué dans d'autres réponses, vous pouvez remplacer ce paramètre dans l'application elle-même ou même une PHP.ini personnalisée que votre conteneur virtualhost ou .htaccess spécifie.

J'espère que cela aide :)


0 commentaires