Bcmath trong PHP là gì?

Mở rộng thư viện php BCMath cho các chức năng bị thiếu như sàn, trần, tròn, abs, min, max, rand cho các số lớn. Cũng bọc các chức năng BCMath hiện có. (thêm http. //php. net/manual/en/book. bc. php)

Cài đặt

composer require krowinski/bcmath-extended

Đặc trưng
  • cấu hình
    • setTrimTrailingZeroes - tắt. bật các số 0 ở cuối (cắt xén mặc định được bật)
  • phương pháp công cụ mới
    • convertScientificNotationToString - chuyển đổi ký hiệu khoa học thành chuỗi
    • getScale - lấy quy mô toàn cầu hiện tại
    • getDecimalsLengthFromNumber - lấy số thập phân
    • hexdec - chuyển đổi từ thập lục phân sang thập phân
    • dechex - chuyển đổi từ thập phân sang thập lục phân
    • bin2dec - chuyển đổi từ nhị phân sang thập phân
    • dec2bin - chuyển đổi từ thập phân sang nhị phân
  • các hàm toán học mới
    • vòng
    • cơ bụng
    • rand
    • tối đa
    • tối thiểu
    • làm tròn xuống
    • làm tròn
    • trònMột nửaChẵn
    • trần nhà
    • kinh nghiệm
    • đăng nhập
    • sự thật
    • pow (hỗ trợ phân số)
    • mod (hỗ trợ phân số + tỷ lệ trong php 5. 6 <)
    • toán tử bitwise
      • bitXor
      • bitHoặc
      • bitAnd
  • proxy cho các chức năng ban đầu (http. //php. net/manual/en/book. bc. php)
  • tất cả các chức năng hỗ trợ ký hiệu khoa học
  • tất cả các chức năng là tĩnh, vì vậy nó có thể dễ dàng thay thế bằng lib này
Thông tin

Kể từ ngày 7. 2 float có thể được chuyển đến bcmod, nhưng chúng không trả về giá trị chính xác (IMO)

Tôi đã tạo lỗi cho điều này trong https. // lỗi. php. mạng/lỗi. php?id=76287, nhưng nó đã được nhận xét là vấn đề về tài liệu không phải lỗi

PHP (từ viết tắt đệ quy của PHP. Bộ tiền xử lý siêu văn bản) là ngôn ngữ kịch bản mã nguồn mở có mục đích chung được sử dụng rộng rãi, đặc biệt phù hợp để phát triển web và có thể được nhúng vào HTML

Khi xử lý các số điểm cố định, bạn phải rất cẩn thận – đặc biệt nếu bạn phát triển bằng PHP và MySQL. Trong bài viết này, các trở ngại và sự phức tạp khi làm việc với phần mở rộng PHP BCMath, xử lý biểu thức điểm cố định của MySQL và duy trì dữ liệu điểm cố định từ PHP sang MySQL được mô tả. Bất chấp những rào cản đang xảy ra, chúng tôi cố gắng tìm ra cách làm việc với các số điểm cố định và không để mất một chữ số nào

Rắc rối với BCMath

Tài liệu BCMath nói

Đối với toán học chính xác tùy ý, PHP cung cấp Máy tính nhị phân hỗ trợ các số có kích thước và độ chính xác bất kỳ, được biểu thị dưới dạng chuỗi

Vì vậy, các tham số hàm BCMath phải được biểu diễn dưới dạng chuỗi. Việc chuyển các giá trị số cho

77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss
2 có thể dẫn đến kết quả sai, mất độ chính xác tương tự như khi chúng ta coi giá trị kép là chuỗi

Trường hợp 1

echo bcmul(776.210000, '100', 10) . PHP_EOL;
    echo bcmul(776.211000, '100', 10) . PHP_EOL;
    echo bcmul(776.210100, '100', 10) . PHP_EOL;

    echo bcmul(50018850776.210000, '100', 10) . PHP_EOL;
    echo bcmul(50018850776.211000, '100', 10) . PHP_EOL;
    echo bcmul(50018850776.210100, '100', 10) . PHP_EOL;

Kết quả là

77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss

Không bao giờ chuyển các giá trị số cho các hàm BCMath, chỉ các giá trị chuỗi đại diện cho các số. Ngay cả khi không xử lý các điểm động, BCMath có thể tạo ra các kết quả lạ

trường hợp 2

echo bcmul('10', 0.0001, 10) . PHP_EOL;
	echo bcmul('10', 0.00001, 10) . PHP_EOL;
	echo 10*0.00001 . PHP_EOL;

Kết quả là

0.0010
	0 // thats really strange!!!
	0.0001

Lý do cho điều này là BCMath chuyển đổi các đối số của nó thành chuỗi và có những trường hợp biểu diễn chuỗi của một số có ký hiệu hàm mũ

Trường hợp 3

echo bcmul('10', '1e-4', 10) . PHP_EOL; //outputs 0 as well

PHP là một ngôn ngữ được gõ yếu và trong một số trường hợp, bạn không thể kiểm soát đầu vào một cách chặt chẽ – bạn muốn xử lý càng nhiều yêu cầu càng tốt

Ví dụ: chúng ta có thể “sửa chữa” Trường hợp 2 và Trường hợp 3 bằng cách áp dụng biến đổi

77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss
3

________số 8

nhưng việc áp dụng cùng một phép biến đổi có thể phá vỡ hành vi “đúng đắn” của Trường hợp 1

$val = sprintf("%.10f", '50018850776.2100000000');
	echo bcmul('10', $val, 10) . PHP_EOL;
	echo bcmul('10', 50018850776.2100000000, 10) . PHP_EOL;
	500188507762.0999908450 //WRONG
	500188507762.10 //RIGHT

Vì vậy lời giải của

77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss
3 không phù hợp với BCmath. Giả sử tất cả đầu vào của người dùng là chuỗi, chúng tôi có thể triển khai trình xác thực đơn giản, nắm bắt tất cả các số ký hiệu hàm mũ và chuyển đổi chúng đúng cách. Kỹ thuật này được thực hiện trong php-bignumbers, vì vậy chúng ta có thể chuyển các đối số như
77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss
5 và
77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss
6 một cách an toàn mà không làm mất đi độ chính xác

77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss
3

Nhưng hai dòng cuối cùng của ví dụ cho chúng ta thấy rằng không thể tránh được các cảnh báo về dấu phẩy động bằng cách phân tích cú pháp đầu vào (điều này hoàn toàn hợp lý – chúng ta không thể xử lý biểu diễn kép bên trong PHP)

hướng dẫn cuối cùng của BCMath

Không bao giờ sử dụng số dấu phẩy động làm đối số hoạt động của điểm cố định. Chuyển đổi chuỗi không giúp được gì, vì chúng tôi không thể quản lý việc mất độ chính xác theo bất kỳ cách nào

Khi sử dụng các phép toán mở rộng BCMath, hãy cẩn thận với các đối số trong biểu diễn hàm mũ. Các hàm BCMath không xử lý các đối số hàm mũ (i. e. '1e-8') một cách chính xác, vì vậy bạn nên chuyển đổi chúng theo cách thủ công. Hãy cẩn thận, không sử dụng

77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss
3 hoặc các kỹ thuật chuyển đổi tương tự, vì nó dẫn đến mất độ chính xác

Bạn có thể sử dụng thư viện php-bignumbers xử lý các đối số đầu vào ở dạng hàm mũ và cung cấp cho người dùng các hàm phép toán điểm cố định. Tuy nhiên, hiệu suất của nó kém hơn so với tiện ích mở rộng BCMath, do đó, đây là một kiểu thỏa hiệp giữa gói mạnh mẽ và hiệu suất

MySQL và số điểm cố định

Trong MySQL, số điểm cố định được xử lý với loại cột

77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss
8. Bạn có thể đọc tài liệu MySQL chính thức để biết các loại dữ liệu và phép toán chính xác

Phần thú vị nhất là cách MySQL xử lý các biểu thức

Việc xử lý một biểu thức số phụ thuộc vào loại giá trị mà biểu thức chứa

Nếu có bất kỳ giá trị gần đúng nào, thì biểu thức là gần đúng và được đánh giá bằng cách sử dụng số học dấu phẩy động

Nếu không có giá trị gần đúng, biểu thức chỉ chứa giá trị chính xác. Nếu bất kỳ giá trị chính xác nào chứa một phần phân số (giá trị sau dấu thập phân), thì biểu thức được đánh giá bằng số học chính xác DECIMAL và có độ chính xác là 65 chữ số. Thuật ngữ "chính xác" phải tuân theo các giới hạn của những gì có thể được biểu diễn dưới dạng nhị phân. Ví dụ: 1. 0/3. 0 có thể được xấp xỉ bằng ký hiệu thập phân là. 333…, nhưng không được viết dưới dạng một số chính xác, vì vậy (1. 0/3. 0)*3. 0 không đánh giá chính xác 1. 0

Mặt khác, biểu thức chỉ chứa các giá trị nguyên. Biểu thức này chính xác và được đánh giá bằng cách sử dụng số học số nguyên và có độ chính xác giống như BIGINT (64 bit)

Nếu một biểu thức số chứa bất kỳ chuỗi nào, chúng sẽ được chuyển đổi thành các giá trị dấu phẩy động có độ chính xác kép và biểu thức là gần đúng

Đây là một ví dụ ngắn thể hiện các trường hợp phần phân số

77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss
6

Nó có thể thấy khá đơn giản, nhưng hãy xem cách xử lý nó trong PHP

Toán chính xác trong PHP & MySQL

Vì vậy, bây giờ chúng ta phải duy trì các giá trị điểm cố định của mình từ PHP vào MySQL. Cách đúng đắn là sử dụng các câu lệnh và trình giữ chỗ đã chuẩn bị sẵn trong các truy vấn của chúng tôi. Sau đó, chúng tôi thực hiện ràng buộc tham số và mọi thứ đều an toàn và bảo mật

77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss
7

Khi chúng tôi liên kết một giá trị với một trình giữ chỗ câu lệnh, chúng tôi có thể chỉ định loại của nó bằng đối số thứ ba

77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss
9. Các kiểu có thể được biểu diễn bằng các hằng số
echo bcmul('10', 0.0001, 10) . PHP_EOL;
	echo bcmul('10', 0.00001, 10) . PHP_EOL;
	echo 10*0.00001 . PHP_EOL;
0,
echo bcmul('10', 0.0001, 10) . PHP_EOL;
	echo bcmul('10', 0.00001, 10) . PHP_EOL;
	echo 10*0.00001 . PHP_EOL;
1,
echo bcmul('10', 0.0001, 10) . PHP_EOL;
	echo bcmul('10', 0.00001, 10) . PHP_EOL;
	echo 10*0.00001 . PHP_EOL;
2,
echo bcmul('10', 0.0001, 10) . PHP_EOL;
	echo bcmul('10', 0.00001, 10) . PHP_EOL;
	echo 10*0.00001 . PHP_EOL;
3,
echo bcmul('10', 0.0001, 10) . PHP_EOL;
	echo bcmul('10', 0.00001, 10) . PHP_EOL;
	echo 10*0.00001 . PHP_EOL;
4 và
echo bcmul('10', 0.0001, 10) . PHP_EOL;
	echo bcmul('10', 0.00001, 10) . PHP_EOL;
	echo 10*0.00001 . PHP_EOL;
5. Vì vậy, vấn đề là phần mở rộng PHP PDO không có loại tham số thập phân để liên kết. Kết quả là, tất cả các biểu thức toán học trong các truy vấn được coi là biểu thức dấu phẩy động, không phải là biểu thức dấu phẩy động

77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss
0

Nếu chúng ta muốn tận dụng các câu lệnh đã chuẩn bị sẵn và làm việc với các số điểm cố định, cách tốt nhất là thực hiện tất cả các phép toán trong PHP và lưu kết quả vào MySQL

77621.00
    77621.100
    77621.0100
    5001885077621.00
    5001885077621.100
    5001885077621.00 //here we can see precision loss
1

Phần kết luận

Chúng tôi đã đi đến kết luận sau

  • Không bao giờ sử dụng các số dấu phẩy động làm đối số hoạt động của điểm cố định trong các hàm mở rộng BCMath PHP. Chỉ chuỗi
  • Tiện ích mở rộng BCMath không hoạt động với chuỗi số trong biểu diễn hàm mũ
  • MySQL hỗ trợ các biểu thức số điểm cố định, nhưng tất cả các toán hạng phải ở định dạng thập phân. Nếu ít nhất một đối số ở định dạng hàm mũ hoặc chuỗi, thì nó được coi là số dấu phẩy động và biểu thức được đánh giá là số dấu phẩy động
  • Tiện ích mở rộng PDO của PHP không có loại tham số
    echo bcmul('10', 0.0001, 10) . PHP_EOL;
    	echo bcmul('10', 0.00001, 10) . PHP_EOL;
    	echo 10*0.00001 . PHP_EOL;
    6, vì vậy nếu bạn sử dụng các câu lệnh đã chuẩn bị sẵn và các tham số ràng buộc trong biểu thức SQL có chứa toán hạng điểm cố định – bạn sẽ không nhận được kết quả chính xác
  • Để thực hiện các phép toán chính xác trong các ứng dụng PHP+MySQL, bạn có thể chọn hai cách. Đầu tiên là xử lý tất cả các hoạt động trong PHP và duy trì dữ liệu cho MySQL chỉ bằng các câu lệnh
    echo bcmul('10', 0.0001, 10) . PHP_EOL;
    	echo bcmul('10', 0.00001, 10) . PHP_EOL;
    	echo 10*0.00001 . PHP_EOL;
    7 hoặc
    echo bcmul('10', 0.0001, 10) . PHP_EOL;
    	echo bcmul('10', 0.00001, 10) . PHP_EOL;
    	echo 10*0.00001 . PHP_EOL;
    8. Trong trường hợp này, bạn có thể sử dụng các câu lệnh đã chuẩn bị và ràng buộc tham số. Cách thứ hai là xây dựng các truy vấn SQL theo cách thủ công (bạn vẫn có thể sử dụng các câu lệnh đã chuẩn bị, nhưng bạn phải tự thoát các tham số) để tất cả các biểu thức toán học SQL đều ở dạng biểu diễn số thập phân

Cách tiếp cận yêu thích cá nhân của tôi là cách đầu tiên. tất cả các phép toán trong PHP. Tôi đồng ý rằng PHP và MySQL có thể không phải là lựa chọn tốt nhất cho các ứng dụng có toán học chính xác, nhưng nếu bạn chọn ngăn xếp công nghệ này, thật tốt khi biết rằng có một cách để giải quyết nó đúng cách

Chia sẻ bài viết này

Bcmath trong PHP là gì?

Aleksey Asiutin

Aleksey là Kỹ sư phần mềm chuyên nghiệp đến từ Ukraine với bằng Thạc sĩ Toán ứng dụng. Anh ấy đã làm việc ở nhiều vị trí khác nhau từ nhà phát triển frontend cấp dưới đến trưởng bộ phận phát triển. Bây giờ anh ấy đang làm việc trong công ty sản phẩm về các dự án Bitcoin và giảng dạy Khoa học Máy tính tại Đại học Kỹ thuật Quốc gia ở Kharkov, Ukraine. Anh ấy thích đóng góp cho các dự án nguồn mở và học hỏi những điều mới (Coursera và Edx là những địa điểm yêu thích của anh ấy)