Asked  9 Months ago    Answers:  5   Viewed   57 times

I am generating 10 random floats between 6 and 8 (all for good reason), and writing them to a mysql database in a serialized form. But one quirk seems to emerge at the storage time:

Before storing I'm just outputting the same data to see what it looks like, and this is the result I get

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

 Answers

40

A number like 6.2 can't be represented exactly using floating-point math in computers as there is no finite base-2 representation of it. What you are seeing when echo-ing the number is something intended for human reading, and thus the value will be rounded to what floats can provide in accuracy (about 6 decimal places for 32-bit and 17 for 64-bit FP values).

When serializing those values, however, you really want the exact value (i. e. all bits that are in there) and not just the nearest "nice" value. There could be more than one float/double representation which evaluates to approximately 6.2 and when serializing you usually really want to store he exact values to the last bit you are having in order to restore them correctly. That's why you're getting ridiculous "accuracy" in values there. It's all just to preserve the exact bit representation of what you started with.

But why exactly do you want to control the serialized output that tightly? I mean, it's just there so you can round-trip your data structure and read it back in later. You certainly don't want to use that serialized representation somewhere in output for humans or so. So if it's just about "nice-looking" values, you shouldn't use serialize which has an entirely different purpose.

Wednesday, March 31, 2021
 
dmp
answered 9 Months ago
dmp
69

There's no difference between the PHP versions that would cause this. There are differences between different mysql drivers, however, and that could cause the issue that you are seeing.

Homestead comes with php5-mysqlnd installed, which is the "MySql Native Driver". When using this driver, floating points and integers fetched from the database will be assigned as numeric datatypes in PHP. If you are not using the native driver (php5-mysql), floating points and integers fetched from the database will be assigned as strings in PHP.

The following code demonstrates how this affects your output:

$f = 9.99000;
$s = "9.99000";

echo $f; // shows 9.99
echo $s; // shows 9.99000

You can check to see if the server is using the native driver with the command php -i | grep mysqlnd. This is just searching through your phpinfo() for any mention of the native driver. If this doesn't return anything, then you are not using the native driver, and your numeric data will be returned as strings.

If you do not have the native driver installed, you will need to remove the old driver and install the new driver:

apt-get remove php5-mysql

apt-get install php5-mysqlnd

Assuming this was your issue in the first place, this will fix it. You can also check out this question and answer for more information.

Wednesday, March 31, 2021
 
jsuissa
answered 9 Months ago
55

I want to know whether the PHP serialize function is 100% secure

this function has nothing to do with security at all. It's just a way to represent complex data structure as a string. that's all.
Database security is another matter, completely indifferent to the source of the form of the data.

if we store serialized data into a database and want to do something after fetching it, will it be a nice way.

Definitely NOT
This way you are spoiling the whole neat idea of the relational databases, which you are supposedly using.
A data should be present in the database in the Normal form, means atomic - each entity occupying distinct cell. Not a whole bunch of unstructured unaccessible data with no possibility of establishing any relation with other data.

what's happening in this code

nothing sensible.

To answer the question from the title

  • Do not store serialized data in mysql
  • Create a table/structure of tables that represents the data structure of your application, keeping all the data bits separated and accessible.

I AM NOT GOING TO USE SERIALIZED DATA FOR SEARCHING

This is but a delusion, from the lack of experience.

Welcome back in couple months, with a question "help me to redone my setup! it is going out of control!"

Wednesday, March 31, 2021
 
osondoar
answered 9 Months ago
90

Include:

#define ARC4RANDOM_MAX 0x100000000

And then try this:

double val = ((double)arc4random() / ARC4RANDOM_MAX) 
   * (maxRange - minRange)
   + minRange;
Wednesday, September 22, 2021
 
laukok
answered 3 Months ago
36

OP has 2 issues:

  1. How to started the sequence very randomly.

  2. How to generate a double on the [0...1) range.

The usual method is to take a very random source like /dev/urandom or the result from the syscall() or maybe even seed = time() ^ process_id; and seed via srand(). Then call rand() as needed.

Below includes a quickly turned method to generate a uniform [0.0 to 1.0) (linear distribution). But like all random generating functions, really good ones are base on extensive study. This one simply calls rand() a few times based on DBL_MANT_DIG and RAND_MAX,

[Edit] Original double rand_01(void) has a weakness in that it only generates a 2^52 different doubles rather than 2^53. It has been amended. Alternative: a double version of rand_01_ld(void) far below.

#include <assert.h>
#include <float.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

double rand_01(void) {
  assert(FLT_RADIX == 2); // needed for DBL_MANT_DIG
  unsigned long long limit = (1ull << DBL_MANT_DIG) - 1;
  double r = 0.0;
  do {
    r += rand();
    // Assume RAND_MAX is a power-of-2 - 1
    r /= (RAND_MAX/2 + 1)*2.0;
    limit = limit / (RAND_MAX/2 + 1) / 2;
  } while (limit);

  // Use only DBL_MANT_DIG (53) bits of precision.
  if (r < 0.5) {
    volatile double sum = 0.5 + r;
    r = sum - 0.5;
  }
  return r;
}

int main(void) {
  FILE *istream = fopen("/dev/urandom", "rb");
  assert(istream);
  unsigned long seed = 0;
  for (unsigned i = 0; i < sizeof seed; i++) {
    seed *= (UCHAR_MAX + 1);
    int ch = fgetc(istream);
    assert(ch != EOF);
    seed += (unsigned) ch;
  }
  fclose(istream);
  srand(seed);

  for (int i=0; i<20; i++) {
    printf("%fn", rand_01());
  }

  return 0;
}

If one wanted to extend to an even wider FP, unsigned wide integer types may be insufficient. Below is a portable method that does not have that limitation.

long double rand_01_ld(void) {
  // These should be calculated once rather than each function call
  // Leave that as a separate implementation problem
  // Assume RAND_MAX is power-of-2 - 1
  assert((RAND_MAX & (RAND_MAX + 1U)) == 0);
  double rand_max_p1 = (RAND_MAX/2 + 1)*2.0;
  unsigned BitsPerRand = (unsigned) round(log2(rand_max_p1));
  assert(FLT_RADIX != 10);
  unsigned BitsPerFP = (unsigned) round(log2(FLT_RADIX)*LDBL_MANT_DIG);

  long double r = 0.0;
  unsigned i;
  for (i = BitsPerFP; i >= BitsPerRand; i -= BitsPerRand) {
    r += rand();
    r /= rand_max_p1;
  }
  if (i) {
    r += rand() % (1 << i);
    r /= 1 << i;
  }
  return r;
}
Friday, October 8, 2021
 
BartmanEH
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share