2016-12-24 2 views
10

Вот сценарий Perl:Почему Perl несовместим с округлением 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"; 
} 

Он выводит:

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

Для меня это очень странно, что Perl не согласуется с округлением. Я ожидаю, что и номер заканчивается .255 быть закруглены +0,26 Это верно для 0,255, но это неверно для числа 100,255

Вот цитата из Perl Cookbook http://docstore.mik.ua/orelly/perl/cookbook/ch02_04.htm

Sprintf. Формат f позволяет указать определенное число десятичных мест для округления аргумента. Perl смотрит на следующую цифру: округляет до 5 или больше и округляет в противном случае.

Но я не вижу каких-либо доказательств того, что это правильно в http://perldoc.perl.org/functions/sprintf.html

Является ли это ошибка в Sprintf или Perl Cookbook это неправильно? Если это желательно, почему он работает таким образом?

+0

Похоже также на '% g':' perl -E 'printf "% .3g \ n", 1.505'' дает 1.5, тогда как 'perl -E' printf"% .3g \ n ", 1.506'' дает 1.51. Также оба 'perl -E 'printf"% .2g \ n ", 0.505'' и' perl -E' printf "% .2g \ n", 0.506'' дают 0.51 –

+0

Вы используете два округленных числа. Вместо этого используйте 'say sprintf '% 1 $ .19g =>% 1 $ .2f', $ number;'. Это ответит на ваш вопрос. – ikegami

+0

Re "* Perl смотрит на следующую цифру, округляет, если она равна 5 или больше, и округляет в противном случае. *", Это неточно. Это полностью зависит от базовой библиотеки C, которая может не обязательно использовать этот метод округления. Фактически, на моей сборке gcc она округляется до четности.(Это не относится к вашей проблеме.) – ikegami

ответ

7

Perl использует базовую библиотеку C для форматирования. То, что эта библиотека может варьироваться от платформы к платформе. Даже POSIX говорит, что «младшая цифра должна быть округлена в соответствии с реализацией».

В glibc, которая, возможно, используется большинством PERL двоичных файлов там, то поведение, которое вы увидите, будет зависеть от нескольких вещей:

Во-первых, как было указано в другом ответе, значение вы думаете округляется, не может быть точно представимо в плавающей точке, и каким образом округление будет определено, если оно является следующим более высоким или меньшим представимым числом.

Во-вторых, даже если значение точно представлено как на полпути между двумя возможными округлениями, glibc будет использовать округление банкира. То есть, он округляется до четной цифры. Таким образом, sprintf '%.1g', .25 будет производить .2, но sprintf '%.1g', .75 будет производить .8.

Цитата из Cookbook Perl просто неверна.

+0

Я бы не сказал, что книга была просто неправильной. В нем говорится: «Обычные процедуры печати в Perl отображают номера от 0 до 15 знаков после запятой» *. Поэтому было правильно, что цифры были округлены, но не упоминается округление банкира и что округление может быть зависимым от реализации. –

+1

@ HåkonHægland Я имел в виду, что «Perl смотрит на следующую цифру, округляется, если ее 5 или больше, и наоборот округляется». – ysth

17

Если добавить эту строку:

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

перед печатью, вы будете иметь:

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

, как вы можете видеть, 100.255 не совсем 100.255 это связано с представлением чисел с плавающей точкой.

+4

Более подробную информацию можно найти в [Глава 2.3] (http://docstore.mik.ua/orelly/perl4/cook/ch02_04.htm) Perl Cookbook: * «Обычные процедуры печати в Perl отображают числа, округленные до 15 знаков после запятой или около того, но его числовые тесты не округляются». * –

+0

этот ответ неверен. – ysth

Смежные вопросы