如何在 Perl 中对浮点数进行四舍五入?

发布于 2024-07-07 23:02:20 字数 83 浏览 8 评论 0原文

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

例如

1.2 = 1
1.7 = 2

How can I round a decimal number (floating point) to the nearest integer?

e.g.

1.2 = 1
1.7 = 2

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

失去的东西太少 2024-07-14 23:02:20

perldoc -q 轮

Perl 有 round() 函数吗? ceil() 和 Floor() 怎么样?
三角函数?

请记住 int() 只是向 0 截断。 要四舍五入到特定位数,sprintf()printf() 通常是最简单的
路线。

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

POSIX 模块(标准 Perl 发行版的一部分)实现
ceil()floor() 以及许多其他数学和三角函数
功能。

使用POSIX; 
      $ceil = ceil(3.5);   #4 
      $地板=地板(3.5);   #3 
  

在 5.000 到 5.003 perls 中,三角函数是在 Math::Complex< /a>
模块。 在 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 
  

不要责怪珀尔。 与 C 中的相同。IEEE 说我们必须这样做
这。 Perl 数字的绝对值是 2**31 下的整数(在
32 位机器)的工作方式与数学整数非常相似。
其他数字不保证。

Output of perldoc -q round

Does Perl have a round() function? What about ceil() and floor()?
Trig functions?

Remember that int() merely truncates toward 0. For rounding to a certain number of digits, sprintf() or printf() is usually the easiest
route.

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

The POSIX module (part of the standard Perl distribution) implements
ceil(), floor(), and a number of other mathematical and trigonometric
functions.

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

In 5.000 to 5.003 perls, trigonometry was done in the Math::Complex
module. With 5.004, the Math::Trig module (part of the standard Perl
distribution) implements the trigonometric functions. Internally it
uses the Math::Complex module and some functions can break out from the
real axis into the complex plane, for example the inverse sine of 2.

Rounding in financial applications can have serious implications, and
the rounding method used should be specified precisely. In these
cases, it probably pays not to trust whichever system rounding is being
used by Perl, but to instead implement the rounding function you need
yourself.

To see why, notice how you'll still have an issue on half-way-point
alternation:

    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

Don't blame Perl. It's the same as in C. IEEE says we have to do
this. Perl numbers whose absolute values are integers under 2**31 (on
32 bit machines) will work pretty much like mathematical integers.
Other numbers are not guaranteed.

小嗲 2024-07-14 23:02:20

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

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

更新

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

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

通过此计算,-1.4 舍入为 -1,-1.6 舍入为 -2,并且零不会爆炸。

Whilst not disagreeing with the complex answers about half-way marks and so on, for the more common (and possibly trivial) use-case:

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

UPDATE

If it's possible for your $float to be negative, the following variation will produce the correct result:

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

With this calculation -1.4 is rounded to -1, and -1.6 to -2, and zero won't explode.

半透明的墙 2024-07-14 23:02:20

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

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

或者您也可以粗略地进行方式:

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

You can either use a module like Math::Round:

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

Or you can do it the crude way:

my $rounded = sprintf "%.0f", $float;
深巷少女 2024-07-14 23:02:20

如果您决定使用 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

If you decide to use printf or sprintf, note that they use the Round half to even method.

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
贱人配狗天长地久 2024-07-14 23:02:20

请参阅 perldoc/perlfaq

请记住,int() 只是向 0 截断。对于舍入到 a
一定数量的数字,sprintf()printf() 通常是
最简单的路线。

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

POSIX 模块(标准 Perl 发行版的一部分)
实现 ceil()floor() 和许多其他数学函数
和三角函数。

使用 POSIX; 
  $ceil = ceil(3.5);   #4 
  $地板=地板(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 说我们必须这样做
这。 绝对值为 2**31 以下整数的 Perl 数字(在
32 位机器)的工作方式与数学整数非常相似。
其他数字无法保证。

See perldoc/perlfaq:

Remember that int() merely truncates toward 0. For rounding to a
certain number of digits, sprintf() or printf() is usually the
easiest route.

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

The POSIX module (part of the standard Perl distribution)
implements ceil(), floor(), and a number of other mathematical
and trigonometric functions.

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

In 5.000 to 5.003 perls, trigonometry was done in the Math::Complex module.

With 5.004, the Math::Trig module (part of the standard Perl distribution) > implements the trigonometric functions.

Internally it uses the Math::Complex module and some functions can break
out from the real axis into the complex plane, for example the inverse sine of 2.

Rounding in financial applications can have serious implications, and the rounding
method used should be specified precisely. In these cases, it probably pays not to
trust whichever system rounding is being used by Perl, but to instead implement the
rounding function you need yourself.

To see why, notice how you'll still have an issue on half-way-point alternation:

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

Don't blame Perl. It's the same as in C. IEEE says we have to do
this. Perl numbers whose absolute values are integers under 2**31 (on
32 bit machines) will work pretty much like mathematical integers.
Other numbers are not guaranteed.

晌融 2024-07-14 23:02:20

您不需要任何外部模块。

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

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

我可能没有理解你的观点,但我认为这是完成同样工作的更干净的方法。

其作用是遍历元素中的每个正数,以您提到的格式打印数字和舍入整数。 该代码仅根据小数连接相应的四舍五入正整数。 int($_) 基本上是对数字进行四舍五入,因此 ($-int($)) 捕获小数。 如果小数(根据定义)严格小于 0.5,则将数字向下舍入。 如果不是,则向上舍入加 1。

You don't need any external module.

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

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

I may be missing your point, but I thought this was much cleaner way to do the same job.

What this does is to walk through every positive number in the element, print the number and rounded integer in the format you mentioned. The code concatenates respective rounded positive integer only based on the decimals. int($_) basically round-down the number so ($-int($)) captures the decimals. If the decimals are (by definition) strictly less than 0.5, round-down the number. If not, round-up by adding 1.

深海里的那抹蓝 2024-07-14 23:02:20

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

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

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

The following will round positive or negative numbers to a given decimal position:

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

    return (int($x / $a + (($x < 0) ? -0.5 : 0.5)) * $a);
}
白况 2024-07-14 23:02:20

以下是对值求和的五种不同方法的示例。 第一种是执行求和的简单方法(并且失败)。 第二个尝试使用 sprintf(),但也失败了。 第三个成功使用了 sprintf(),而最后两个(第四和第五)则使用了 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 的依赖。

Following is a sample of five different ways to summate values. The first is a naive way to perform the summation (and fails). The second attempts to use sprintf(), but it too fails. The third uses sprintf() successfully while the final two (4th & 5th) use 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

Note that floor($value + 0.5) can be replaced with int($value + 0.5) to remove the dependency on POSIX.

违心° 2024-07-14 23:02:20

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

printf 风格的方法为我们提供了正确的数字,但它们可能会导致一些奇怪的显示。 我们发现这个方法(在我看来是愚蠢的)放入了一个 - 符号,无论它是否应该。 例如,-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;

Negative numbers can add some quirks that people need to be aware of.

printf-style approaches give us correct numbers, but they can result in some odd displays. We have discovered that this method (in my opinion, stupidly) puts in a - sign whether or not it should or shouldn't. For example, -0.01 rounded to one decimal place returns a -0.0, rather than just 0. If you are going to do the printf style approach, and you know you want no decimal, use %d and not %f (when you need decimals, it's when the display gets wonky).

While it's correct and for math no big deal, for display it just looks weird showing something like "-0.0".

For the int method, negative numbers can change what you want as a result (though there are some arguments that can be made they are correct).

The int + 0.5 causes real issues with -negative numbers, unless you want it to work that way, but I imagine most people don't. -0.9 should probably round to -1, not 0. If you know that you want negative to be a ceiling rather than a floor then you can do it in one-liner, otherwise, you might want to use the int method with a minor modification (this obviously only works to get back whole numbers:

my $var = -9.1;
my $tmpRounded = int( abs($var) + 0.5));
my $finalRounded = $var >= 0 ? 0 + $tmpRounded : 0 - $tmpRounded;
我不在是我 2024-07-14 23:02:20

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

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

If you are only concerned with getting an integer value out of a whole floating point number (i.e. 12347.9999 or 54321.0001), this approach (borrowed and modified from above) will do the trick:

my $rounded = floor($float + 0.1); 
你的背包 2024-07-14 23:02:20

我的 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 );

My solution for 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 );
转瞬即逝 2024-07-14 23:02:20

在阅读大量有关如何对数字进行舍入的文档后,许多专家建议编写自己的舍入例程,因为随您的语言提供的“固定”版本可能不够精确或包含错误。 然而,我想他们谈论的是许多小数位,而不仅仅是一位、两位或三位。 考虑到这一点,这是我的解决方案(虽然不完全按照我的需要显示美元的要求 - 但过程没有太大不同)。

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

loads of reading documentation on how to round numbers, many experts suggest writing your own rounding routines, as the 'canned' version provided with your language may not be precise enough, or contain errors. i imagine, however, they're talking many decimal places not just one, two, or three. with that in mind, here is my solution (although not EXACTLY as requested as my needs are to display dollars - the process is not much different, though).

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);
}
日久见人心 2024-07-14 23:02:20

使用 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

Using Math::BigFloat you can do something like this:

use Math::BigFloat;

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

This can be wrapped in a subroutine

use Math::BigFloat;

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

print round(1.2); ## 1
print round(1.7); ## 2
漫雪独思 2024-07-14 23:02:20
cat table |
  perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log($1)/log(2))."\t$2\n";' 
cat table |
  perl -ne '/\d+\s+(\d+)\s+(\S+)/ && print "".**int**(log($1)/log(2))."\t$2\n";' 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文