Perl에서 부동 소수점 숫자를 어떻게 반올림합니까?


174

소수 (부동 소수점)를 가장 가까운 정수로 반올림하는 방법은 무엇입니까?

예 :

1.2 = 1
1.7 = 2

답변:


196

출력 perldoc -q round

Perl에 round () 함수가 있습니까? ceil ()과 floor ()는 어떻습니까? 삼각 함수?

int()향해 잘립니다 0. 특정 자릿수로 반올림 sprintf()하거나 printf()보통 가장 쉬운 경로입니다.

    printf("%.3f", 3.1415926535);       # prints 3.142

POSIX모듈 (표준 펄 분포의 일부)을 구현 ceil(), floor()및 다른 수학적 삼각 함수의 개수.

    use POSIX;
    $ceil   = ceil(3.5);                        # 4
    $floor  = floor(3.5);                       # 3

5.000 ~ 5.003 perls에서 삼각법이 Math::Complex 모듈 에서 수행되었습니다 . 5.004에서는 Math::Trig표준 Perl 분포의 일부인 모듈이 삼각 함수를 구현합니다. 내부적으로 Math::Complex모듈을 사용 하며 일부 함수는 실제 축에서 복잡한 평면으로 나올 수 있습니다 (예 : 역 사인 2).

재무 응용 프로그램의 반올림은 심각한 영향을 줄 수 있으며 사용 된 반올림 방법을 정확하게 지정해야합니다. 이 경우 아마도 Perl이 사용하는 시스템 라운딩을 신뢰하지 않고 대신 라운딩 기능을 구현해야합니다.

이유를 확인하려면 중간 지점 교대에 여전히 문제가있는 방법에 주목하십시오.

    for ($i = 0; $i < 1.01; $i += 0.05) { printf "%.1f ",$i}

    0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7
    0.8 0.8 0.9 0.9 1.0 1.0

Perl을 비난하지 마십시오. IEEE와 마찬가지로 C도 마찬가지다. 절대 값이 2**31(32 비트 시스템에서) 정수인 펄 숫자는 수학 정수와 매우 유사하게 작동합니다. 다른 번호는 보장되지 않습니다.


17
^ Thariama, 왜 천장이 더 이상 사용되지 않습니까? 내가 아는 한 POSIX 또는 perl에서는 더 이상 사용되지 않습니다. 인용이 필요했습니다!
Sam Watkins

3
@Beginners, printf변수에 결과를 원한다면 사용하지 마십시오. sprintf이것은 디버깅 시간을 절약하기를 바랍니다
:-P

int()PDL에 사용할 수 있습니까 ?
CinCout

1
POSIX를 사용하십시오. <br/> $ x = ($ x-floor ($ x)> = .5)? ceil ($ x) : 바닥 ($ x);
Joseph Argenio

136

보다 일반적인 (사소한) 유스 케이스의 경우 중간 표시 등에 대한 복잡한 답변에 동의하지 않는 경우 :

my $rounded = int($float + 0.5);

최신 정보

$float음수가 될 수 있으면 다음과 같은 변형으로 올바른 결과를 얻을 수 있습니다.

my $rounded = int($float + $float/abs($float*2 || 1));

이 계산으로 -1.4는 -1로 반올림되고 -1.6은 -2로 반올림되며 0은 폭발하지 않습니다.


4
...하지만 그것은 음수에 실패 : 여전히 더 나은 sprintf
alessandro

2
아뇨, 그렇지 않습니다. 음수를 반올림하면 더 멀리 떨어지지 않고 0에 가까워집니다. 요즘 학교에서 무엇을 가르치고 있습니까?
RET

6
@RET 예, 음수로 실패합니다. 이 방법으로 $ float = -1.4의 결과는 0입니다. 그것이 그들이 내 학교에서 가르치는 것이 아닙니다. int ()는 0쪽으로 잘립니다.
fishinear

4
@fishinear 당신은 정확하고 정직하게 징계됩니다. 그러나 나는 '사소한 사용 사례'라고 말했습니다. 내 답변이 수정되었습니다.
RET

1
$ float = 0이면 실패합니다 :-)
mat

74

Math :: Round 와 같은 모듈을 사용할 수 있습니다 .

use Math::Round;
my $rounded = round( $float );

또는 당신은 그것을 거친 방법으로 할 수 있습니다 :

my $rounded = sprintf "%.0f", $float;

46

printf 또는 sprintf를 사용하기로 결정한 경우 반올림을 짝수로 사용합니다.

foreach my $i ( 0.5, 1.5, 2.5, 3.5 ) {
    printf "$i -> %.0f\n", $i;
}
__END__
0.5 -> 0
1.5 -> 2
2.5 -> 2
3.5 -> 4

이것을 지적 해 주셔서 감사합니다. 보다 정확하게는이 방법의 이름은 '반올림에서 짝수로'입니다.
Jean Vincent

printf 또는 sprintf를 언급하는 모든 대답은 이것을 언급해야합니다.
insaner

이것은 매우 중요한 정보입니다. 5는 항상 반올림된다고 가정했기 때문에 소프트웨어에 서버 시간 버그가있었습니다. 나는 마침내 펄이 내가 원하는 것을하지 않은 이유를 발견했다. 이것을 지적 해 주셔서 감사합니다.
Boris Däppen

실제로 이것은 OS에 따라 다릅니다! : Windows에서 그것은 라운드 반 0에서 먼과 유닉스와 같은 뜻 라운드의 절반에도 윌 exploringbinary.com/...
에이 팍스

9

perldoc / perlfaq 참조 :

그 기억 int()단지 숫자의 특정 번호로 라운딩 0으로 절단, sprintf()또는 printf()일반적으로 가장 쉬운 방법입니다.

 printf("%.3f",3.1415926535);
 # prints 3.142

POSIX모듈 (표준 펄 분포의 일부)을 구현 ceil(), floor()및 다른 수학적 삼각 함수의 개수.

use POSIX;
$ceil  = ceil(3.5); # 4
$floor = floor(3.5); # 3

5.000 ~ 5.003 perls에서 삼각법이 Math::Complex모듈 에서 수행되었습니다 .

5.004에서는 Math::Trig모듈 (표준 Perl 분포의 일부)> 삼각 함수를 구현합니다.

내부적으로 Math::Complex모듈을 사용 하며 일부 함수는 실제 축에서 복잡한 평면으로 나올 수 있습니다 (예 : 역 사인 2).

재무 응용 프로그램의 반올림은 심각한 영향을 줄 수 있으며 사용 된 반올림 방법을 정확하게 지정해야합니다. 이 경우 아마도 Perl이 사용하는 시스템 라운딩을 신뢰하지 않고 대신 라운딩 기능을 구현해야합니다.

이유를 확인하려면 중간 지점 교대에 여전히 문제가있는 방법에 주목하십시오.

for ($i = 0; $i < 1.01; $i += 0.05)
{
   printf "%.1f ",$i
}

0.0 0.1 0.1 0.2 0.2 0.2 0.3 0.3 0.4 0.4 0.5 0.5 0.6 0.7 0.7 0.8 0.8 0.9 0.9 1.0 1.0

Perl을 비난하지 마십시오. IEEE와 마찬가지로 C도 마찬가지다. 절대 값이 2 ** 31 (32 비트 시스템) 미만의 정수인 Perl 숫자는 수학 정수와 매우 유사하게 작동합니다. 다른 번호는 보장되지 않습니다.


3

외부 모듈이 필요하지 않습니다.

$x[0] = 1.2;
$x[1] = 1.7;

foreach (@x){
  print $_.' = '.( ( ($_-int($_))<0.5) ? int($_) : int($_)+1 );
  print "\n";
}

나는 당신의 요점을 놓칠 수도 있지만, 이것이 같은 일을하는 것이 훨씬 깔끔한 방법이라고 생각했습니다.

이것이하는 일은 요소의 모든 양수를 살펴보고 언급 한 형식으로 숫자와 반올림 된 정수를 인쇄하는 것입니다. 이 코드는 소수를 기준으로 한 반올림 된 양의 정수를 연결합니다. int ($ _)는 기본적으로 숫자를 내림 하므로 ($ -int ($ ))는 소수를 캡처합니다. 소수가 (정의 적으로) 엄격하게 0.5보다 작 으면 숫자를 내림하십시오. 그렇지 않은 경우 1을 추가하여 반올림하십시오.


1
다시 한번, RET의 대답과 같은 것이 똑같이 잘 작동 할 때 복잡한 대답으로 고대 질문에 대답하는 이유는 무엇입니까?
Joel Berger

1
이것은 매우 복잡하지 않으며 RET의 대답에는 a) 이론적으로 오버플로가 발생할 위험이 있고 b) 시간이 오래 걸리며 c) 불필요하게 더 많은 fp 부정확성을 최종 값에 도입하는 수학이 포함됩니다. 잠깐, 어느 것이 다시 복잡합니까? ;)
cptstubing06

2

다음은 양수 또는 음수를 주어진 소수 자릿수로 반올림합니다.

sub round ()
{
    my ($x, $pow10) = @_;
    my $a = 10 ** $pow10;

    return (int($x / $a + (($x < 0) ? -0.5 : 0.5)) * $a);
}

1

다음은 값을 요약하는 5 가지 방법의 샘플입니다. 첫 번째는 합산을 수행하고 실패하는 순진한 방법입니다. 두 번째는을 사용하려고 sprintf()하지만 실패합니다. 세 번째는 sprintf()성공적으로 사용 하고 마지막 두 번째 (4 및 5)는을 사용 floor($value + 0.5)합니다.

 use strict;
 use warnings;
 use POSIX;

 my @values = (26.67,62.51,62.51,62.51,68.82,79.39,79.39);
 my $total1 = 0.00;
 my $total2 = 0;
 my $total3 = 0;
 my $total4 = 0.00;
 my $total5 = 0;
 my $value1;
 my $value2;
 my $value3;
 my $value4;
 my $value5;

 foreach $value1 (@values)
 {
      $value2 = $value1;
      $value3 = $value1;
      $value4 = $value1;
      $value5 = $value1;

      $total1 += $value1;

      $total2 += sprintf('%d', $value2 * 100);

      $value3 = sprintf('%1.2f', $value3);
      $value3 =~ s/\.//;
      $total3 += $value3;

      $total4 += $value4;

      $total5 += floor(($value5 * 100.0) + 0.5);
 }

 $total1 *= 100;
 $total4 = floor(($total4 * 100.0) + 0.5);

 print '$total1: '.sprintf('%011d', $total1)."\n";
 print '$total2: '.sprintf('%011d', $total2)."\n";
 print '$total3: '.sprintf('%011d', $total3)."\n";
 print '$total4: '.sprintf('%011d', $total4)."\n";
 print '$total5: '.sprintf('%011d', $total5)."\n";

 exit(0);

 #$total1: 00000044179
 #$total2: 00000044179
 #$total3: 00000044180
 #$total4: 00000044180
 #$total5: 00000044180

에 대한 종속성을 제거하기 위해 floor($value + 0.5)대체 될 수 있습니다 .int($value + 0.5)POSIX


1

음수는 사람들이 알아야 할 단점을 추가 할 수 있습니다.

printf스타일 접근 방식은 정확한 숫자를 제공하지만 이상한 표시가 발생할 수 있습니다. 우리는이 방법 (내 의견으로는 어리석게도) -이해 야하는지 아닌지를 표시합니다. 당신이 할하려는 경우 예를 들어, -0.01 소수 첫째 자리에서 반올림이 아니라 단지 0보다는 -0.0 반환 printf스타일의 접근 방식을, 당신은 더 소수를 싶지 알고, 사용 %d하지%f 당신이 소수를 필요로 할 때, 그 때의 ( 디스플레이가 ky니다).

정확하고 수학에는 별다른 문제가 없지만, 디스플레이에서는 "-0.0"과 같은 것이 이상하게 보입니다.

int 메소드의 경우 음수는 결과로 원하는 것을 변경할 수 있습니다 (올바르게 만들 수있는 인수가 있지만).

int + 0.5당신이 그런 식으로 일을하지 않으려면, 제외 어 번호와 실제 문제가 발생하지만 대부분의 사람들이하지 않는 상상한다. -0.9는 0이 아닌 -1로 반올림해야합니다. 음수가 바닥이 아니라 천장이되기를 원한다면 1- 라이너로 수행 할 수 있습니다. 수정 (이것은 분명히 정수를 되 찾는 것입니다.

my $var = -9.1;
my $tmpRounded = int( abs($var) + 0.5));
my $finalRounded = $var >= 0 ? 0 + $tmpRounded : 0 - $tmpRounded;

0

sprintf에 대한 나의 솔루션

if ($value =~ m/\d\..*5$/){
    $format =~ /.*(\d)f$/;
    if (defined $1){
       my $coef = "0." . "0" x $1 . "05";    
            $value = $value + $coef;    
    }
}

$value = sprintf( "$format", $value );

0

전체 부동 소수점 숫자 (예 : 12347.9999 또는 54321.0001)에서 정수 값만 가져 오려는 경우이 접근 방식 (위에서 차용 및 수정)이 트릭을 수행합니다.

my $rounded = floor($float + 0.1); 

0

숫자를 반올림하는 방법에 대한 많은 양의 문서를 읽으면, 많은 전문가들은 언어와 함께 제공된 '통조림'버전이 정확하지 않거나 오류를 포함 할 수 있으므로 자신의 반올림 루틴을 작성하는 것이 좋습니다. 그러나 그들은 단지 하나, 둘 또는 셋이 아닌 많은 소수점 자리를 말하고 있다고 상상합니다. 그것을 염두에두고, 여기 내 솔루션이 있습니다 (필요로 요청 한대로 달러를 표시 해야하는 것은 아니지만 프로세스는 크게 다르지 않습니다).

sub asDollars($) {
  my ($cost) = @_;
  my $rv = 0;

  my $negative = 0;
  if ($cost =~ /^-/) {
    $negative = 1;
    $cost =~ s/^-//;
  }

  my @cost = split(/\./, $cost);

  # let's get the first 3 digits of $cost[1]
  my ($digit1, $digit2, $digit3) = split("", $cost[1]);
  # now, is $digit3 >= 5?
  # if yes, plus one to $digit2.
  # is $digit2 > 9 now?
  # if yes, $digit2 = 0, $digit1++
  # is $digit1 > 9 now??
  # if yes, $digit1 = 0, $cost[0]++
  if ($digit3 >= 5) {
    $digit3 = 0;
    $digit2++;
    if ($digit2 > 9) {
      $digit2 = 0;
      $digit1++;
      if ($digit1 > 9) {
        $digit1 = 0;
        $cost[0]++;
      }
    }
  }
  $cost[1] = $digit1 . $digit2;
  if ($digit1 ne "0" and $cost[1] < 10) { $cost[1] .= "0"; }

  # and pretty up the left of decimal
  if ($cost[0] > 999) { $cost[0] = commafied($cost[0]); }

  $rv = join(".", @cost);

  if ($negative) { $rv = "-" . $rv; }

  return $rv;
}

sub commafied($) {
  #*
  # to insert commas before every 3rd number (from the right)
  # positive or negative numbers
  #*
  my ($num) = @_; # the number to insert commas into!

  my $negative = 0;
  if ($num =~ /^-/) {
    $negative = 1;
    $num =~ s/^-//;
  }
  $num =~ s/^(0)*//; # strip LEADING zeros from given number!
  $num =~ s/0/-/g; # convert zeros to dashes because ... computers!

  if ($num) {
    my @digits = reverse split("", $num);
    $num = "";

    for (my $i = 0; $i < @digits; $i += 3) {
      $num .= $digits[$i];
      if ($digits[$i+1]) { $num .= $digits[$i+1]; }
      if ($digits[$i+2]) { $num .= $digits[$i+2]; }
      if ($i < (@digits - 3)) { $num .= ","; }
      if ($i >= @digits) { last; }
    }

    #$num =~ s/,$//;
    $num = join("", reverse split("", $num));
    $num =~ s/-/0/g;
  }

  if ($negative) { $num = "-" . $num; }

  return $num; # a number with commas added
  #usage: my $prettyNum = commafied(1234567890);
}

서브 루틴이 사양에 맞도록하려면 다음을 수정하십시오. if ($digit3 >= 5) { $digit3 = 0; $digit2++; if ($digit2 > 9) { $digit2 = 0; $digit1++; if ($digit1 > 9) { $digit1 = 0; $cost[0]++; } } } 그러면 다음과 같습니다. if ($digit1 >= 5) { $digit1 = 0; $cost[0]++; }return commafied($cost[0]);
Jarett Lloyd

-2
cat table |
  perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log($1)/log(2))."\t$2\n";' 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.