186

如何将十进制数(浮点数)四舍五入到最接近的整数?

例如

1.2 = 1
1.7 = 2
4

14 回答 14

206

的输出perldoc -q round

Perl 有 round() 函数吗?ceil() 和 floor() 呢?三角函数?

请记住,int()仅向 截断0。用于四舍五入到一定位数,sprintf()或者printf()通常是最简单的方法。

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

POSIX模块(标准 Perl 发行版的一部分)实现 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。这与 C 中的相同。IEEE 说我们必须这样做。绝对值是整数的 Perl 数字2**31(在 32 位机器上)将非常像数学整数一样工作。不保证其他数字。

于 2008-10-07T13:54:12.120 回答
148

虽然不同意关于中途标记等的复杂答案,但对于更常见(并且可能微不足道)的用例:

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

更新

如果您的结果可能为$float负,则以下变体将产生正确的结果:

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

通过这个计算,-1.4 舍入为 -1,-1.6 舍入为 -2,零不会爆炸。

于 2009-08-13T21:26:05.420 回答
78

您可以使用Math::Round 之类的模块:

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

或者你可以用粗略的方式来做:

my $rounded = sprintf "%.0f", $float;
于 2008-10-07T13:54:09.920 回答
47

如果您决定使用 printf 或 sprintf,请注意他们使用Round half to even方法。

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
于 2008-10-07T15:37:02.927 回答
9

请参阅perldoc/perlfaq

请记住,它int()只是向 0 截断。用于四舍五入到一定数量的数字,sprintf()或者printf()通常是最简单的方法。

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

POSIX模块(标准 Perl 发行版的一部分)实现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。这与 C 中的相同。IEEE 说我们必须这样做。Perl 数字的绝对值是小于 2**31 的整数(在 32 位机器上),其工作方式与数学整数非常相似。不保证其他数字。

于 2008-10-07T13:58:29.757 回答
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 进行舍入。

于 2011-06-13T20:51:58.523 回答
2

以下会将正数或负数四舍五入到给定的小数位:

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

    return (int($x / $a + (($x < 0) ? -0.5 : 0.5)) * $a);
}
于 2014-09-12T14:13:38.203 回答
1

以下是对值求和的五种不同方法的示例。第一种是执行求和(并且失败)的幼稚方式。第二次尝试使用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.

于 2010-12-21T21:06:50.170 回答
1

负数会增加一些人们需要注意的怪癖。

printf-style 方法为我们提供了正确的数字,但它们可能会导致一些奇怪的显示。我们发现这种方法(在我看来是愚蠢的)-表明它是否应该或不应该。例如,四舍五入到小数点后一位的 -0.01 会返回 -0.0,而不仅仅是 0。如果您要使用printf样式方法,并且您知道不需要小数,请使用%d和不使用%f(当您需要小数时,就在显示变得不稳定)。

虽然它是正确的并且对于数学来说没什么大不了的,但对于显示来说,它看起来很奇怪,显示像“-0.0”这样的东西。

对于 int 方法,负数可以改变你想要的结果(尽管有一些参数可以使它们是正确的)。

int + 0.5除非您希望它以这种方式工作,否则会导致负数的真正问题,但我想大多数人都不会。-0.9 应该四舍五入到 -1,而不是 0。如果您知道要让负数成为天花板而不是地板,那么您可以在单线中做到这一点,否则,您可能需要使用带有小数的 int 方法修改(这显然只能取回整数:

my $var = -9.1;
my $tmpRounded = int( abs($var) + 0.5));
my $finalRounded = $var >= 0 ? 0 + $tmpRounded : 0 - $tmpRounded;
于 2011-08-22T22:38:37.807 回答
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 );
于 2011-11-09T11:35:35.910 回答
0

如果您只关心从整个浮点数(即 12347.9999 或 54321.0001)中获取整数值,那么这种方法(从上面借用和修改)可以解决问题:

my $rounded = floor($float + 0.1); 
于 2016-09-23T19:47:44.850 回答
0

使用Math::BigFloat您可以执行以下操作:

use Math::BigFloat;

print Math::BigFloat->new(1.2)->bfround(1); ## 1
print Math::BigFloat->new(1.7)->bfround(1); ## 2

这可以包装在一个子例程中

use Math::BigFloat;

sub round {
  Math::BigFloat->new(shift)->bfround(1);
}

print round(1.2); ## 1
print round(1.7); ## 2
于 2021-05-12T04:05:41.767 回答
-1

大量阅读有关如何舍入数字的文档,许多专家建议编写自己的舍入例程,因为您的语言提供的“罐装”版本可能不够精确,或者包含错误。然而,我想他们说的是许多小数位,而不仅仅是一位、两位或三位。考虑到这一点,这是我的解决方案(尽管并不完全符合我的要求,因为我需要显示美元——不过,这个过程并没有太大的不同)。

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);
}
于 2019-12-07T10:55:26.987 回答
-2
cat table |
  perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log($1)/log(2))."\t$2\n";' 
于 2009-08-13T14:25:56.847 回答