2016-12-24 20 views
10

Dưới đây là một kịch bản perl:Tại sao Perl không phù hợp với làm tròn sprintf?

#!/usr/bin/perl 

use strict; 
use warnings; 
use feature 'say'; 

my @numbers = qw(
    0.254 
    0.255 
    0.256 
); 

foreach my $number (@numbers) { 
    my $rounded = sprintf '%.2f', $number; 
    say "$number => $rounded"; 
} 

foreach my $number (@numbers) { 
    $number += 100; 
    my $rounded = sprintf '%.2f', $number; 
    say "$number => $rounded"; 
} 

Nó ra:

0.254 => 0.25 
0.255 => 0.26 
0.256 => 0.26 
100.254 => 100.25 
100.255 => 100.25 
100.256 => 100.26 

Đối với tôi nó là rất lạ rằng Perl là không phù hợp với làm tròn. Tôi hy vọng rằng cả hai số kết thúc với 0,255 để được làm tròn như 0,26 Nó đúng cho 0,255, nhưng nó là sai cho số 100,255

Dưới đây là trích dẫn từ Perl Cookbook http://docstore.mik.ua/orelly/perl/cookbook/ch02_04.htm

sprintf. Định dạng f cho phép bạn chỉ định một số thập phân cụ thể là số để làm tròn đối số của nó. Perl nhìn vào chữ số sau, làm tròn lên nếu số đó là 5 hoặc lớn hơn và làm tròn xuống theo cách khác.

Nhưng tôi không thể nhìn thấy bất kỳ bằng chứng cho thấy nó là chính xác trong http://perldoc.perl.org/functions/sprintf.html

Có một lỗi trong sprintf hoặc Perl Cookbook là sai? Nếu đó là hành vi mong muốn tại sao nó hoạt động theo cách này?

+0

Dường như cũng ảnh hưởng đến '% g':' perl -E 'printf "% .3g \ n", 1.505'' cho 1,5, trong khi «perl -E' printf"% .3g \ n ", 1.506'' cho 1,51. Ngoài ra cả hai «perl -E 'printf"% .2g \ n ", 0,505'' và' perl -E' printf "% .2g \ n", 0,506'' cho 0,51 –

+0

Bạn đang sử dụng hai số được làm tròn. Thay vào đó, hãy sử dụng 'say sprintf '% 1 $ .19g =>% 1 $ .2f', $ number;'. Điều này sẽ trả lời câu hỏi của bạn. – ikegami

+0

Re "* Perl nhìn vào chữ số sau đây, làm tròn lên nếu nó là 5 hoặc lớn hơn, và làm tròn xuống nếu không. *", Điều này là không chính xác. Nó hoàn toàn dựa vào thư viện C cơ sở, mà có thể không nhất thiết phải sử dụng phương thức làm tròn đó. Trong thực tế, trên gcc xây dựng của tôi, nó làm tròn đến thậm chí.(Tuy nhiên, điều này không liên quan đến vấn đề của bạn.) – ikegami

Trả lời

7

Perl sử dụng thư viện C cơ bản để định dạng. Thư viện này có thể thay đổi từ nền tảng này sang nền tảng khác. Ngay cả POSIX cũng nói "Chữ số thứ tự thấp sẽ được làm tròn theo cách thức được thực hiện."

Trong glibc, được cho rằng đã được sử dụng bởi hầu hết các mã nhị phân perl trên mạng, hành vi mà bạn thấy sẽ bị ảnh hưởng bởi một vài điều:

Thứ nhất, như đã chỉ ra trong câu trả lời khác, giá trị mà bạn nghĩ đang được làm tròn có thể không được biểu diễn chính xác trong dấu phẩy động, và cách thức làm tròn đi sẽ được xác định bởi nếu nó là số đại diện cao hơn hoặc thấp hơn tiếp theo.

Thứ hai, ngay cả khi giá trị chính xác được thể hiện là nửa chừng giữa hai vòng có thể, glibc sẽ sử dụng làm tròn của ngân hàng. Tức là, nó sẽ tròn thành một chữ số chẵn. Vì vậy, sprintf '%.1g', .25 sẽ sản xuất .2, nhưng sprintf '%.1g', .75 sẽ sản xuất .8.

Trích dẫn từ sách dạy nấu ăn Perl chỉ đơn giản là sai.

+0

Tôi sẽ không nói rằng cuốn sách đã sai. Nó nói * "Thường trình in thường lệ của Perl hiển thị số _rounded_ đến 15 chữ số thập phân" *. Vì vậy, nó đã được chính xác trong đó các con số được hiển thị tròn, nhưng nó không đề cập đến làm tròn của ngân hàng và làm tròn có thể được thực hiện phụ thuộc. –

+1

@ HåkonHægland Tôi có nghĩa là 'Perl nhìn vào chữ số sau đây, làm tròn lên nếu nó là 5 hoặc lớn hơn, và làm tròn xuống khác.' – ysth

17

Nếu bạn thêm dòng này:

$number = sprintf '%.15f', $number; 

trước khi in, bạn sẽ có:

0.254000000000000 => 0.25 
0.255000000000000 => 0.26 
0.256000000000000 => 0.26 
100.254000000000005 => 100.25 
100.254999999999995 => 100.25 
100.256000000000000 => 100.26 

như bạn có thể thấy, 100.255 là không chính xác 100.255 này là do đại diện các số float.

+4

Chi tiết khác có thể tìm thấy trong [Chương 2.3] (http://docstore.mik.ua/orelly/perl4/cook/ch02_04.htm) của Perl Cookbook: * "Các thường trình in bình thường của Perl hiển thị số được làm tròn đến 15 chữ số thập phân hoặc hơn, nhưng các phép thử số của nó không tròn." * –

+0

Câu trả lời này không chính xác. – ysth

Các vấn đề liên quan