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.