Perl代码使用until循环迭代从0.1到0.9失败的原因是什么?

huangapple go评论57阅读模式
英文:

Why does my Perl code fail to iterate from 0.1 to 0.9 using until loop?

问题

为什么它不起作用?

#!/usr/bin/env perl

use strict;
use warnings;

my $float = 0.1;
until ($float == 1) {
    print("$float\n");
    $float += 0.1;
    sleep 1;
}

它会继续打印 1、1.1、1.2 等等,但如果我将条件改为 until ($float == 0.2),它会执行一次 0.1 的迭代然后退出循环。

我想要打印从 0.1 到 0.9。

英文:

Why it doesn't work?

#!/usr/bin/env perl

use strict;
use warnings;

my $float = 0.1;
until ($float == 1) {
    print("$float\n");
    $float += 0.1;
    sleep 1;
}

It goes further 1, 1.1, 1.2 and so on, but if I give until ($float == 0.2) it makes one iteration 0.1 and quit from loop.

I'd like to print from 0.1 to 0.9

答案1

得分: 4

1/10 在二进制中是一个周期性数字,就像 1/3 在十进制中一样,因此不能通过浮点数精确表示。

$ perl -e 'printf "%.100g\n", 0.1'
0.1000000000000000055511151231257827021181583404541015625

通过重复将其加在一起,您永远不会得到精确的 1

$ perl -e 'my $acc = 0; for ( 1 .. 11 ) { $acc += 0.1; printf "%.100g\n", $acc }'
0.1000000000000000055511151231257827021181583404541015625
0.200000000000000011102230246251565404236316680908203125
0.3000000000000000444089209850062616169452667236328125
0.40000000000000002220446049250313080847263336181640625
0.5
0.59999999999999997779553950749686919152736663818359375
0.6999999999999999555910790149937383830547332763671875
0.79999999999999993338661852249060757458209991455078125
0.899999999999999911182158029987476766109466552734375
0.99999999999999988897769753748434595763683319091796875
1.0999999999999998667732370449812151491641998291015625

一个解决方法是使用十分之一。

my $tenths = 1;
until ( $tenths == 10 ) {
    my $float = $tenths / 10;
    say $float;
    ++$tenths;
    sleep 1;
}

简化版本:

for my $tenths ( 1 .. 9 ) {
   my $float = $tenths / 10;
   say $float;
   sleep 1;
}

进一步简化:

for my $tenths ( 1 .. 9 ) {
   say "0.$tenths";
   sleep 1;
}
英文:

1/10 is a periodic number in binary like 1/3 is in decimal, so it can't be represented exactly by a floating point number.

$ perl -e'printf "%.100g\n", 0.1'
0.1000000000000000055511151231257827021181583404541015625

By repeatedly adding that to itself, you never get exactly 1.

$ perl -e'my $acc = 0; for ( 1 .. 11 ) { $acc += 0.1; printf "%.100g\n", $acc }'
0.1000000000000000055511151231257827021181583404541015625
0.200000000000000011102230246251565404236316680908203125
0.3000000000000000444089209850062616169452667236328125
0.40000000000000002220446049250313080847263336181640625
0.5
0.59999999999999997779553950749686919152736663818359375
0.6999999999999999555910790149937383830547332763671875
0.79999999999999993338661852249060757458209991455078125
0.899999999999999911182158029987476766109466552734375
0.99999999999999988897769753748434595763683319091796875
1.0999999999999998667732370449812151491641998291015625

One solution is to work with tenths.

my $tenths = 1;
until ( $tenths == 10 ) {
    my $float = $tenths / 10;
    say $float;
    ++$tenths;
    sleep 1;
}

Simplified:

for my $tenths ( 1 .. 9 ) {
   my $float = $tenths / 10;
   say $float;
   sleep 1;
}

Further simplified:

for my $tenths ( 1 .. 9 ) {
   say "0.$tenths";
   sleep 1;
}

huangapple
  • 本文由 发表于 2023年6月2日 10:09:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/76386723.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定