如何在Fortran浮点输出中填充前导零?

9

我有一些浮点数需要从Fortran程序输出。假设最大值可能为999.9999,它们都是非负的。对于所有小于100的数字,我需要在前面填充零。

例如,如果我有25.6893782、245.354567和1.2345678,我需要以以下形式打印出它们:

025.6894
245.3546
001.2346

我该如何做到这一点?如果我知道所有数字都在10到99之间,那么使用T编辑描述符就相当容易了。但是事先我无法知道这一点。

5个回答

13

这对我有效

real :: areal

然后

write(*,'(i3.3,f0.6)') int(areal),areal-int(areal)

在继续思考后,我想到了这样的解决方案,但是你的方法更加优雅。谢谢。 - bob.sacamento
这种方法在处理像 2.999999999999 这样的数字时会失败。由于四舍五入,这将变得尴尬。 - kvantour

2

对于整数字段可以进行零填充,因此如果将结果打印为两个单独的字段,则可能会使其发生。这里有一种不太美观但有效的方法。假设x是您要打印的值:

DOUBLE PRECISION x
CHARACTER*6 y

x = 123.4567

WRITE(y,'(F6.4)') x-int(x)


WRITE(*,'(I3.3,A5)') int(x), y(2:5)
y 被声明为 CHARACTER*6,因为它需要容纳您的数字的小数部分(4位小数),一个前导零和一个小数点。如果您想显示更多的小数位数,这很容易改变,但如果您想显示可变数量的小数位数,则会更棘手。 I3.3 字段描述符的意思是“打印最大字段宽度为3的整数,并在左侧填充零,以确保始终有3个数字”。在打印值时,我们将使用 y(2:5) 来去除前导零。
祝编码愉快!

我想到了类似的东西。你和我基本上正在做High Performance Mark所做的事情,但他的方法更加紧凑。感谢您的帮助。 - bob.sacamento
我看到了High Performance Mark的修复方法并且认同。我之前不知道有关于'0'宽度说明符的事情,但这是一个更好的解决方案。很高兴能提供帮助。 - c.maclean

2
这是我在70年代末在Commodore PET上使用MS BASIC时经常使用的技巧。代码已针对负数进行了修改。如果您想要正数有一个前导" + ",只需将signchr的最后一个字符更改为'+'。
subroutine leadingzero(x)
   real x
   character(len=16) str
   character, dimension(3):: signchr = (/'-', ' ', ' ' /)
   write(str,'(F9.4)') 1000.0 + abs(x)
   write(*,*) signchr(int(sign(1.0,x)) + 2), str(2:) ! drop the 1
end subroutine leadingzero

program main
   call leadingzero(0.01)
   call leadingzero(0.1)
   call leadingzero(2.532)
   call leadingzero(9.9999)
   call leadingzero(9.999999)
   call leadingzero(10.987)
   call leadingzero(123.456)
   call leadingzero(0.0)
   call leadingzero(-0.01)
   call leadingzero(-0.1)
   call leadingzero(-2.532)
   call leadingzero(-9.9999)
   call leadingzero(-9.999999)
   call leadingzero(-10.987)
   call leadingzero(-123.456)
end program

编辑 - 返回字符串结果
subroutine leadingzerostr(x, str_temp)
    real x
    character(*) str_temp
    character(len=10) str
    character, dimension(3):: signchr = (/'-', ' ', ' ' /)
    write(str,'(F10.4)') 10000.0 + abs(x)
    str_temp = str
    str_temp(1:1) = signchr(int(sign(1.0,x)) + 2)
end subroutine leadingzerostr

如果值为负数,你能提供解决方案吗? - user4372504
你期望什么样的结果?它会打印出负数为-000.01,正数为000.01。 - cup
有没有机会展示一下我们如何编写 signchr(int(sign(1.0,x)) + 2), str(2:),例如,str_temp,它是一个长度为 0000.0000字符类型 - user4372504
结果是空字符。尝试使用类似字符结果的 str_temp 函数。 - user4372504

1
另一种方法是使用TLTR位置编辑描述符来代替高性能标记的方法。首先使用Fw.d打印浮点数,向后移动w个位置,使用填充零和宽度为w-d的整数进行打印,向前移动d+1个位置。
write (*, '(F6.3,TL6,I2.2,TR4)') f,int(f)

这种方法和高性能标记的方法存在精度问题。以下程序演示了这一点:
program test_rounding
  double precision :: f
  f = 6 - 1D-6
  ! default compiler dependent rounding :: gfortran NEAREST
  write (*, '(F6.3,TL6,I2.2,TR4)') f,int(f)
  write (*, '(I2.2,F0.3)') int(f), f-int(f)
  write (*, '(I2.2,F4.3)') int(f), f-int(f)
  ! rounding to ZERO
  write (*, '(I2.2,RZ,F4.3)') int(f), f-int(f)
  write (*, '(RZ,F6.3,TL6,I2.2,TR4)') f,int(f)
end program

05.000          < WRONG
051.000         < VERY WRONG
05****          < EUH
05.999          < OFF BY 0.001
05.999          < OFF BY 0.001

最后一种方法可能会引起兴趣,但它并不是真正的期望值。然而,它具有相同的准确性。
以下两种方法按预期工作,但需要手动操作数字以获得所需的精度。这不是人们所期望的:
program test_rounding
  double precision :: f
  f = 6 - 1D-6
  ! manual manipulation
  write (*,'(I2.2,".",I3.3)') nint(f*1D3)/1000, mod(nint(f*1D3),1000)
  write (*,'(F6.3,TL6,I2.2,TR4)') f,nint(f*1D3)/1000
end program

两者都返回 06.000


0

我不认为有一个编辑描述符可以做到你想要的,但你可以用一个if语句来模拟它:

if(var < 10) then
   write(*,'(a,f6.4)') '00',var
else if(var < 100) then
   write(*,'(a,f7.4)') '0',var
else
   write(*,'(f8.4)') var
endif

如果一个值接近于10,比如9.999999,那么write语句会将其格式化为“10.0000”,从而产生“0010.0000”的输出,而不是期望的“010.0000”吗? - Eric Postpischil
它将会输出所需的 010.0000,但如果你有 9.9999,它将会输出 009.9999 - Kyle Kanos
那是怎么工作的?难道不是 9.999999 会通过测试 var < 10,导致写入“00”后跟着“10.0000”吗? - Eric Postpischil
我刚刚测试了一下,它是这样工作的;我猜测这是由于精度问题(使用REAL)导致的。如果我使用9.99999,它会返回00****** - Kyle Kanos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接