Perl'de kayan nokta sayısını nasıl yuvarlarsınız?


174

Ondalık bir sayıyı (kayan nokta) en yakın tam sayıya nasıl yuvarlayabilirim?

Örneğin

1.2 = 1
1.7 = 2

Yanıtlar:


196

Çıktı perldoc -q round

Perl'in round () işlevi var mı? Ceil () ve floor () ne olacak? Trig fonksiyonları?

Unutmayın, int()sadece kısalır 0. Belirli bir sayıya yuvarlamak için sprintf()veya printf()genellikle en kolay yoldur.

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

POSIXModül (standart Perl dağıtım parçası) uygular ceil(), floor()ve diğer matematiksel ve trigonometrik fonksiyonların bir dizi.

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

5.000 ila 5.003 perls değerinde, Math::Complex modülde trigonometri yapıldı . 5.004 ile Math::Trigmodül (standart Perl dağılımının bir parçası) trigonometrik fonksiyonları uygular. Dahili olarak Math::Complexmodülü kullanır ve bazı işlevler gerçek eksenden karmaşık düzleme girebilir, örneğin 2'nin ters sinüsü.

Finansal uygulamalarda yuvarlamanın ciddi sonuçları olabilir ve kullanılan yuvarlama yöntemi tam olarak belirtilmelidir. Bu durumlarda, muhtemelen Perl tarafından hangi sistem yuvarlama kullanılıyorsa güvenmemek, bunun yerine kendinize ihtiyacınız olan yuvarlama işlevini uygulamak için ödeme yapar.

Nedenini görmek için, yarım nokta değişimi konusunda nasıl bir sorununuz olduğunu görün:

    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'i suçlama. C ile aynı. IEEE bunu yapmak zorunda olduğumuzu söylüyor. Mutlak değerleri 2**31(32 bit makinelerde) altında tamsayı olan Perl sayıları, matematiksel tamsayılara çok benzer şekilde çalışır. Diğer numaralar garanti edilmez.


17
^ Thariama, tavan neden reddedildi? Bildiğim kadarıyla POSIX veya perl'de kullanılmıyor. Kaynak belirtilmeli!
Sam Watkins

3
@Başlangıçta, printfbir değişkente sonuç istiyorsanız kullanmayı deneyin , kullanın sprintf... Umarım bu size hata ayıklama süresi kazandırır :-P
Boris Däppen

int()PDL'lerde kullanabilir miyim ?
CinCout

1
POSIX kullanın; <br/> $ x = ($ x - kat ($ x)> = .5)? tavan (x $): kat (x $);
Joseph Argenio

136

Daha çok (ve muhtemelen önemsiz) kullanım durumu için, yarım yol işaretleri ve benzerleri hakkındaki karmaşık cevaplara katılmamaya rağmen:

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

GÜNCELLEME

$floatNegatif olmanız mümkünse , aşağıdaki varyasyon doğru sonucu verecektir:

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

Bu hesaplama ile -1.4, -1'e ve -1.6 ila -2'ye yuvarlanır ve sıfır patlamaz.


4
... ama negatif sayılarda başarısız: hala daha iyi sprintf
alessandro

2
Ah hayır, öyle değil. Negatif bir sayıyı yuvarlamak sizi sıfıra yaklaştırır, daha fazla değil. Bugünlerde okullarda ne öğretiyorlar?
RET

6
@RET Evet, negatif sayılarla başarısız olur. $ float = -1.4 bu yöntemle 0 sonucunu verir. Okulumda öğrettikleri bu değil. İnt () 'nin sıfıra doğru kesildiğini unutmayın.
balıkçılarAra

4
Doğru olduğunuzu ve ben usulüne uygun bir şekilde iffetli. Ama 'önemsiz kullanım davası için' dedim. Cevabım düzeltildi.
RET

1
$ Float = 0 olduğunu unutmayın, bu başarısız olacak :-)
mat

74

Math :: Round gibi bir modül kullanabilirsiniz :

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

Ya da ham yolu yapabilirsiniz:

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

46

Printf veya sprintf kullanmaya karar verirseniz, bunların çift ​​yarıyı eşitleme yöntemini kullandıklarını unutmayın .

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

Bunu işaret ettiğiniz için teşekkürler. Daha doğrusu, yöntemin adı 'Yarım Yarımdan Çift'e'.
Jean Vincent

Printf veya sprintf'den bahseden tüm cevaplar bundan bahsetmelidir.
insaner

Bu çok önemli bir bilgidir. 5 kez her zaman yuvarlatılmış olacağını varsaydım çünkü yazılımda servis süreleri vardı. Sonunda perl neden istediğimi hiç yapmadı. Bunu işaret ettiğiniz için teşekkürler.
Boris Däppen

Aslında, bu işletim sistemine bağlıdır! Windows'da sıfırdan yarıya yuvarlanacak ve unix benzeri yarım bile yuvarlanacak: exploringbinary.com/…
Apoc

9

Bkz. Perldoc / perlfaq :

int()Sadece 0'a doğru kesildiğini unutmayın. Belirli sayıda basamağa yuvarlamak için sprintf()veya printf()genellikle en kolay yoldur.

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

POSIXModül (standart Perl dağıtım parçası) uygular ceil(), floor()ve diğer matematiksel ve trigonometrik fonksiyonların bir dizi.

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

5.000 ila 5.003 perls değerinde, Math::Complexmodülde trigonometri yapıldı .

5.004 ile Math::Trigmodül (standart Perl dağılımının bir parçası)> trigonometrik fonksiyonları uygular.

Dahili olarak Math::Complexmodülü kullanır ve bazı işlevler gerçek eksenden karmaşık düzleme girebilir, örneğin 2'nin ters sinüsü.

Finansal uygulamalarda yuvarlamanın ciddi sonuçları olabilir ve kullanılan yuvarlama yöntemi tam olarak belirtilmelidir. Bu durumlarda, muhtemelen Perl tarafından hangi sistem yuvarlama kullanılıyorsa güvenmemek, bunun yerine kendinize ihtiyacınız olan yuvarlama işlevini uygulamak için ödeme yapar.

Nedenini görmek için, yarım nokta değişimi konusunda nasıl bir sorununuz olduğunu görün:

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'i suçlama. C ile aynı. IEEE bunu yapmak zorunda olduğumuzu söylüyor. Mutlak değerleri 2 ** 31 (32 bit makinelerde) altında tamsayı olan Perl sayıları, matematiksel tamsayılara çok benzer şekilde çalışır. Diğer numaralar garanti edilmez.


3

Harici bir modüle ihtiyacınız yoktur.

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

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

Senin fikrini kaçırıyor olabilirim, ama bunun aynı işi yapmanın çok daha temiz bir yolu olduğunu düşündüm.

Bunun yaptığı, öğedeki her pozitif sayıdan geçmek, sayıyı ve yuvarlanmış tamsayıyı belirttiğiniz biçimde yazdırmaktır. Kod, yalnızca ondalık sayılara dayalı olarak karşılık gelen yuvarlak pozitif tamsayıyı birleştirir. int ($ _) temel olarak sayıyı aşağı yuvarlar, böylece ($ -int ($ )) ondalık sayıları yakalar. Ondalık sayılar (tanım gereği) kesinlikle 0,5'ten küçükse sayıyı yuvarlayın. Değilse, 1 ekleyerek yuvarlayın.


1
Bir kez daha, eski bir soruyu neden RET'in cevabı gibi bir şey eşit derecede iyi çalıştığında karmaşık bir cevapla cevaplıyoruz.
Joel Berger

1
Bu gerçekten çok karmaşık değildir ve RET'in cevabı, a) teorik olarak taşma riskini, b) daha uzun sürdüğünü ve c) gereksiz yere nihai değerinize daha fazla fp tutarsızlığı getirdiği bir grup matematik içerir. Bekle, hangisi tekrar karmaşık? ;)
cptstubing06

2

Aşağıdakiler, pozitif veya negatif sayıları belirli bir ondalık konuma yuvarlar:

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

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

1

Aşağıda değerleri özetlemenin beş farklı yolunun bir örneği verilmiştir. Birincisi, toplamı gerçekleştirmenin naif bir yoludur (ve başarısız olur). İkincisi kullanmaya çalışır sprintf(), ancak başarısız olur. Üçüncüsü sprintf()başarıyla, son iki (4. ve 5.) kullanılır 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

Bağımlılığı kaldırmak için floor($value + 0.5)bununla değiştirilebileceğini unutmayın .int($value + 0.5)POSIX


1

Negatif sayılar, insanların bilmeleri gereken bazı tuhaflıklar ekleyebilir.

printftarzı yaklaşımlar bize doğru sayılar verir, ancak bazı garip görüntülerle sonuçlanabilir. Bu yöntemin (bence, aptalca), -yapması gerekip gerekmediğine dair bir işaret koyduğunu keşfettik . Örneğin, bir ondalık basamağa yuvarlanmış -0,01 yalnızca 0 yerine bir -0,0 döndürür. printfStil yaklaşımını yapacaksanız ve ondalık basamak istemediğinizi biliyorsanız, kullanmayın %dve kullanmayın %f(ondalıklara ihtiyacınız olduğunda, görüntü sakat olur).

Doğru ve matematik için önemli bir şey olmasa da, görüntülemek için sadece "-0.0" gibi bir şey göstermek garip görünüyor.

İnt yöntemi için, negatif sayılar sonuç olarak ne istediğinizi değiştirebilir (doğru olmaları için yapılabilecek bazı argümanlar olsa da).

Bu int + 0.5şekilde çalışmasını istemediğiniz sürece -negatif sayılarla gerçek sorunlara neden olur, ancak çoğu insanın işe yaramadığını hayal ediyorum. -0.9 muhtemelen 0 değil, -1'e yuvarlanmalıdır. Negatifin bir kattan ziyade bir tavan olmasını istediğinizi biliyorsanız, o zaman bir astarda yapabilirsiniz, aksi takdirde, int yöntemini bir minörle kullanmak isteyebilirsiniz. değişiklik (bu sadece tam sayıları geri almak için çalışır:

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

0

Sprintf için çözümüm

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

Yalnızca tam bir kayan nokta sayısından (yani 12347.9999 veya 54321.0001) bir tam sayı değeri elde etmekle ilgileniyorsanız, bu yaklaşım (yukarıdan ödünç alınmış ve değiştirilmiş) hile yapacaktır:

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

0

sayıların nasıl yuvarlanacağına dair birçok okuma belgesi, birçok uzman kendi yuvarlama rutinlerinizi yazmanızı önerir, çünkü dilinizle birlikte verilen 'hazır' sürüm yeterince hassas olmayabilir veya hatalar içerebilir. Ancak, sadece bir, iki veya üç değil, ondalık basamaklardan bahsettiklerini hayal ediyorum. akılda tutulması gereken, benim çözümüm (her ne kadar TAMAMEN talep edilmediğim gibi dolar göstermek için olsa da - süreç çok farklı değil).

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);
}

altyordam spesifikasyonlarınıza uygun hale getirmek için aşağıdakileri değiştirmeniz yeterlidir: if ($digit3 >= 5) { $digit3 = 0; $digit2++; if ($digit2 > 9) { $digit2 = 0; $digit1++; if ($digit1 > 9) { $digit1 = 0; $cost[0]++; } } } bu yüzden: if ($digit1 >= 5) { $digit1 = 0; $cost[0]++; } o zaman sadecereturn commafied($cost[0]);
Jarett Lloyd

-2
cat table |
  perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log($1)/log(2))."\t$2\n";' 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.