2009-07-10 37 views
11

Tôi đang tạo 10 float ngẫu nhiên từ 6 đến 8 (tất cả vì lý do chính đáng) và ghi chúng vào cơ sở dữ liệu mysql dưới dạng tuần tự. Nhưng có một điều không minh bạch dường như xuất hiện đồng thời lưu trữ:PHP - Tuần tự hóa các dấu phẩy động

Trước khi lưu trữ Tôi chỉ xuất ra cùng một dữ liệu để xem những gì nó trông như thế nào, và đây là kết quả tôi nhận được

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

Như bạn có thể nhìn thấy , Tôi nhận được các số dài như 6.20000000000000017763568394002504646778106689453125 thay vì những gì tôi thực sự muốn thấy, chỉ 6.2. Điều này chỉ xảy ra khi tôi tuần tự hóa dữ liệu, nếu tôi chỉ xuất ra mảng, tôi sẽ lấy các số float cho một số thập phân. Đây là mã của tôi:

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

Trông giống như vòng lặp nổi nổi lên, nhưng điều lạ lùng – usoban

Trả lời

15

Một số như 6.2 không thể được biểu diễn chính xác bằng cách sử dụng toán tử dấu phẩy động trong các máy tính vì không có biểu diễn cơ sở 2 hữu hạn nào của nó. Những gì bạn đang nhìn thấy khi echo -ing số là một cái gì đó dành cho con người đọc, và do đó giá trị sẽ được làm tròn để những gì nổi có thể cung cấp chính xác (khoảng 6 chữ số thập phân cho 32-bit và 17 cho 64-bit FP giá trị).

Khi tuần tự hóa các giá trị đó, bạn thực sự muốn giá trị chính xác (ví dụ: tất cả các bit nằm trong đó) và không chỉ là giá trị "đẹp" gần nhất. Có thể có nhiều hơn một đại diện float/double đánh giá xấp xỉ 6.2 và khi tuần tự hóa bạn thường là thực sự muốn lưu trữ giá trị chính xác của anh ấy đến bit cuối cùng mà bạn đang có để khôi phục chúng một cách chính xác. Đó là lý do tại sao bạn đang nhận được "chính xác" vô lý trong các giá trị ở đó. Đó là tất cả chỉ để bảo tồn các đại diện bit chính xác của những gì bạn bắt đầu với.

Nhưng tại sao chính xác bạn muốn kiểm soát đầu ra được tuần tự hóa chặt chẽ? Ý tôi là, nó chỉ ở đó để bạn có thể đi vòng quanh cấu trúc dữ liệu của mình và đọc lại nó sau. Bạn chắc chắn không muốn sử dụng đại diện được tuần tự hóa ở đâu đó trong đầu ra cho con người hay như vậy. Vì vậy, nếu nó chỉ là về "đẹp" giá trị, bạn không nên sử dụng serialize trong đó có một mục đích hoàn toàn khác nhau.

+0

ah, cảm ơn vì lời giải thích, điều đó có thể sẽ đi một chặng đường dài. Lý do tôi muốn giữ cho mảng được tuần tự hóa của tôi đơn giản là vì lo sợ rằng chuỗi nối tiếp dài hơn sẽ tăng tải trên máy chủ truy vấn. Tôi rất muốn biết rằng tôi sai. cảm ơn mọi người khác vì câu trả lời nhanh ... các bạn đá! Chỉ cần để giữ cho mọi thứ đơn giản mặc dù (tức là không có chức năng bổ sung), tôi chỉ có thể gắn bó với giải pháp này, mặc dù Gumbo của bạn là rất thanh lịch là tốt. –

+0

Vâng, cơ sở dữ liệu được tối ưu hóa khá nhiều cho trường hợp sử dụng truy xuất dữ liệu. Cho dù bạn đang đọc 200 byte hoặc 2000 không nên tạo ra nhiều sự khác biệt trừ khi bạn làm điều gì đó thực sự tải dữ dội.Nhưng bạn vẫn có thể tối ưu hóa khi nó chứng tỏ là một nút cổ chai. – Joey

+0

Tôi gặp sự cố này khi lưu trữ dữ liệu được tuần tự hóa. Tôi đã lưu trữ số lượng lớn các bản ghi để sử dụng 58 chữ số để lưu trữ những gì nên có 4 ký tự dường như không thể chấp nhận được. Tôi đã sử dụng giải pháp number_format được cung cấp bởi 'shadowhand' bên dưới. –

3

cửa hàng chúng như số nguyên (shift nơi thập phân đầu tiên ở phía trước của điểm bằng cách nhân nó bằng 10) và chuyển đổi chúng trở lại nếu bạn cần nó:

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

thực sự là giải pháp tốt, nhưng tôi nghĩ tôi sẽ gắn bó với việc đăng ký ngay bây giờ, vì lý do tôi đã viết trong nhận xét ở trên. cảm ơn sự giúp đỡ của bạn mặc dù –

1

Đây là câu trả lời của tôi về câu trả lời của Gumbo. Tôi đặt IteratorAggregate lên đó để nó có thể được cho phép, nhưng bạn cũng có thể thêm Countable và ArrayAccess.

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

cửa hàng chúng như chuỗi sau khi sử dụng number_format:

$number = number_format($float, 2); 
+0

Đây là những gì tôi đã làm để "giải quyết" tính năng này. –

+0

number_format không phải là cách an toàn để chuyển đổi số thành chuỗi. miền địa phương en_US sẽ thêm các dấu phẩy nhóm. "1234.56" trở thành "1,234,56". Thêm một chuỗi rỗng vào số để chuyển đổi. $ number. = ''; – Schien

1

Đối với tôi, tôi tìm thấy 3 cách:

  1. chuyển đổi phao để nguyên sau var phao được nhân với một số lượng lớn (ví ví dụ 1.000.000); nó không phải là một cách rất thuận tiện vì bạn không nên quên chia cho cùng 1.000.000 nó khi nó được sử dụng
  2. để sử dụng preg_replace('/d:([0-9]+(\.[0-9]+)?([Ee][+-]?[0-9]+)?);/e', "'d:'.((float)$1).';'", $value); trong đó $ value là phao của bạn; được tìm thấy here
  3. cũng để làm tròn phao bằng round() và lưu nó trong mảng dưới dạng chuỗi.

Trong mọi trường hợp tôi sử dụng biến thể # 2

1

Đúc cũng làm việc, và nó là nhanh, Ví dụ:

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

string (57) "d: 0.6310000000000000053290705182007513940334320068359375; "

var_dump($c); 

chuỗi (12) "s: 5:" 0.631 ";"

var_dump(unserialize($b)); 

float (0,631)

var_dump(unserialize($c)); 

string (5) "0,631"

Điều quan trọng là để cast nó trở lại unserialize:

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

float (0,631)

1

Chỉ cần giảm bớt precisio n:

ini_set('serialize_precision',2); 
0

PHP.INI tập tin chứa một chỉ thị serialize_precision, cho phép bạn kiểm soát có bao nhiêu chữ số có nghĩa sẽ được tuần tự cho phao của bạn. Trong trường hợp của bạn, lưu trữ chỉ một số thập phân của số từ 6 đến 8 có nghĩa là hai chữ số có nghĩa.

Bạn có thể đặt thiết lập này trong tập tin php.ini hoặc trực tiếp trong kịch bản của bạn:

ini_set('serialize_precision', 2); 

Nếu bạn không quan tâm đến những con số chính xác của con số đáng kể, nhưng quan tâm về việc không có một spaghetti chữ số kết quả từ cách số phao được lưu trữ, bạn cũng có thể cung cấp cho một đi đến một giá trị là -1, mà gọi "thuật toán làm tròn đặc biệt", điều này có thể thực hiện chính xác những gì được yêu cầu:

ini_set('serialize_precision', -1); 

bạn thậm chí có thể thiết lập lại nó trở về giá trị ban đầu sau khi serialization của bạn:

$prec = ini_get('serialize_precision'); 
    ini_set('serialize_precision', -1); 

    ... // your serialization here 

    ini_set('serialize_precision', $prec); 
Các vấn đề liên quan