Fortran - 想要舍入到一个小数点

时间:2016-08-23 05:50:59

标签: fortran rounding gfortran netcdf

在fortran中,我必须将经度和经度舍入到小数点后的一位数。

我正在使用gfortran编译器和nint函数,但以下内容不起作用 -

rlons(i,j) = nint(rla2rlarot*10.0)/10.0

5 个答案:

答案 0 :(得分:4)

正如其他人所说,问题是在 NetCDF 文件中使用浮点表示。使用 let name = nameGlobal.value; // might need to add toString() back let regExp = new RegExp(name, "gi"); let fields = [ xfa.resolveNode("path_to_node")]; for (let field of fields) { let rawValue = field.rawValue if (!rawValue) { // might need to add the !== null field.rawValue = rawValue.replace(regExp, "********"); } } 实用程序,您可以使用 scale_factor 和 add_offset 将纬度/经度更改为短整数。像这样:

nco

答案 1 :(得分:3)

关于“四舍五入到一位小数”,可以考虑几个方面。这些涉及:内部存储和操作;展示和交流。

展示和交流

最简单的方面涵盖了我们如何报告存储值,而不管使用的内部表示如何。正如其他答案和其他地方所深入介绍的那样,我们可以使用带有单个小数位数的数字编辑描述符:

print '(F0.1,2X,F0.1)', 10.3, 10.17
end

输出的舍入方式是可变模式

print '(RU,F0.1,2X,RD,F0.1)', 10.17, 10.17
end

在此示例中,我们选择先向上舍入然后向下舍入,但我们也可以舍入为零或舍入到最接近的位置(或让编译器为我们选择)。

对于任何格式化的输出,无论是屏幕还是文件,都可以使用此类编辑描述符。 G 编辑描述符(例如可能用于写入 CSV 文件的描述符)也将执行此舍入操作。

对于无格式输出,此舍入概念不适用,因为引用了内部表示。同样,对于 NetCDF 和 HDF5 等交换格式,我们没有这种舍入。

对于 NetCDF,您的属性约定可能会指定类似 FORTRAN_format 的内容,它为(默认)实数、非舍入变量的最终显示提供了适当的格式。

内部存储

其他答案和问题本身提到无法准确表示(和使用)十进制数字。然而,Fortran 语言中没有任何东西要求这是​​不可能的:

integer, parameter :: rk = SELECTED_REAL_KIND(radix=10)
real(rk) x
x = 0.1_rk

print *, x

end

是一个 Fortran 程序,它有一个基数为 10 的变量和文字常量。另见IEEE_SELECTED_REAL_KIND(radix=10)

现在,您极有可能看到 selected_real_kind(radix=10) 为您提供值 -5,但是如果您想要一些可以用作类型参数的正数,您只需要找到提供给您这样的值的人一个系统。

如果您找不到这样的东西,那么您将需要努力解决错误。这里有两个部分需要考虑。

Fortran 中的内在实数值类型是浮点类型。要使用定点数字类型或二进制编码十进制等系统,您将需要求助于非内在类型。这样的话题超出了本答案的范围,但 DrOli 指出了这个方向。

这些努力在计算/程序员时间上不会便宜。您还需要在输出和交换中管理这些类型。

根据您的工作要求,您可能会发现只需按 10 的(幂)缩放并处理整数西装。在这种情况下,您还需要在约定中找到相应的 NetCDF 属性,例如 scale_factor

关于我们的内部表示问题,我们对输出有类似的舍入问题。例如,如果我的输入数据的经度为 10.17...,但我想在我的内部表示中将其四舍五入到(最接近的可表示值)一个十进制数字(比如 10.2/{{1} }) 然后解决这个问题,我该如何管理?

我们已经看到 10.1999998 如何为我们提供这一点,但我们还了解了数字编辑描述符如何为输出很好地做到这一点,包括控制舍入模式:

nint(10.17*10)/10.

如果需要,我们可以在此处跟踪错误的累积。

答案 2 :(得分:2)

没有办法做你要问的事。根本问题是您想要的舍入值不一定能够使用浮点表示。

例如,如果您的值为10.58,则在IEEE754 float32中将其精确表示为1.3225000 x 2 ^ 3 = 10.580000。

当你将其四舍五入到一个小数点(但是你选择这样做)时,结果将是10.6,但是10.6没有精确的表示。 float32中最近的表示为1.3249999 x 2 ^ 3 = 10.599999。因此,无论您如何处理舍入,都无法将10.6精确地存储在float32值中,也无法将其作为浮点值写入netCDF文件。

答案 3 :(得分:2)

`round_x = nint(x*10d0)/10d0' 运算符对 x 进行舍入(对于 abs(x) < 2**31/10,对于大数使用 dnint())并将舍入后的值分配给 round_x 变量用于进一步计算。

如上面的答案所述,并非所有小数点后有一位有效数字的数字都有精确表示,例如 0.3 就没有。

print *, 0.3d0

输出:

  0.29999999999999999

要将舍入后的值输出到文件、屏幕或将其转换为小数点后有一个有效数字的字符串,请使用编辑描述符“Fw.1”(w - 宽度 w 字符,0 -可变宽度)。例如:

print '(5(1x, f0.1))', 1.30, 1.31, 1.35, 1.39, 345.46

输出:

 1.3 1.3 1.4 1.4 345.5

@JohnE,使用'G10.2' 是不正确的,它将结果四舍五入到两位有效数字,而不是小数点后一位。例如:

print '(g10.2)',  345.46

输出:

<块引用>

0.35E+03

附言

对于 NetCDF,舍入应由 NetCDF 查看器处理,但是,您可以将变量输出为 NC_STRING 类型:

write(NetCDF_out_string, '(F0.1)') 1.49

或者,获得“漂亮”的 NC_FLOAT/NC_DOUBLE 数字:

beautiful_float_x = nint(x*10.)/10. + epsilon(1.)*nint(x*10.)/10./2.
beautiful_double_x = dnint(x*10d0)/10d0 + epsilon(1d0)*dnint(x*10d0)/10d0/2d0

P.P.S. @JohnE<​​/p>

  1. 首选的解决方案是不要舍入内存或文件中的中间结果。仅当人类可读数据的最终输出发出时才进行舍入;

  2. 使用带有编辑描述符“Fw.1”的打印,见上文;

  3. 没有简单可靠的方法来准确存储四舍五入的数字(带有小数固定点的数字):

2.1.理论上,一些 Fortran 实现可以支持十进制算术,但我不知道“selected_real_kind(4, 4, 10)”返回 -5 以外的值的实现;

2.2.可以将四舍五入的数字存储为字符串;

2.3.您可以使用 GIMP 库的 Fortran 绑定。带有 mpq_ 前缀的函数旨在处理有理数;

  1. 没有简单可靠的方法可以在 netCDF 文件中写入四舍五入的数字,同时为该文件的阅读者保留其属性:

3.1. netCDF 支持“打包数据值”,即您可以使用属性“scale_factor”、“add_offset”设置整数类型并保存整数数组。但是,在文件“scale_factor”中将存储为单精度或双精度的浮点数,即该值将与 0.1 不同。相应地,在读取时,通过netCDF库unpacked_data_value = packed_data_value*scale_factor + add_offset进行计算时,会出现舍入误差。 (您可以设置 scale_factor=0.1*(1.+epsilon(1.))scale_factor=0.1d0*(1d0+epsilon(1d0)) 以排除大量数字“9”。);

3.2.有 C_format 和 FORTRAN_format 属性。但是很难预测哪个读者会使用哪个属性以及他们是否会使用它们;

3.3.您可以将四舍五入的数字存储为字符串或用户定义的类型;

  1. 使用带有编辑描述符“Fw.1”的 write(),见上文。

答案 4 :(得分:0)

是的,可以完成!上面的“接受”答案在其有限的范围内是正确的,但在Fortran(或其他各种HGL)中你可以实现的目标是错误的。

唯一的问题是,如果像F(6.1)这样的东西失败了,你愿意支付多少价格?

从一个角度来看,您的问题在“任意精度”计算主题上是一个特别微不足道的变化。当你需要以精确的精度存储,操作和执行1024位数字的“数学”时,你怎么想象加密?

在这种情况下,一个简单的策略是将每个数字分成其组成部分“LHSofD”(十进制的左手侧)和“RHSofD”值。例如,您可能有一个RLon(i,j)= 105.591,并希望打印105.6(或任何方式的舍入)到您的netCDF(或任何正常)文件。将其拆分为RLonLHS(i,j)= 105,并且RLonRHS(i,j)= 591。

......此时你有选择可以提高普遍性,但需要付出一些代价。为了节省“钱”,RHS可能会被保留为0.591(但如果你需要做更好的事情,则可以保持松散的一般性)。

为简单起见,假设“廉价而愉快”的第二种策略。

LHS很简单(Int())。

现在,对于RHS,乘以10(如果,你希望舍入到1 DEC),例如到达RLonRHS(i,j)= 5.91,然后将Fortran“舍入到最近的Int”NInt()内在...留下RLonRHS(i,j)= 6.0。

......鲍勃是你的叔叔:

现在使用连接“双重”的合适的Write语句将LHS和RHS打印到netCDF,并根据OP中的所需目标创建一个精确的表示。

...当然稍后读入这些值会返回到上面说明的相同问题,除非读入也是ArbPrec识别。

...我们编写了我们自己的ArbPrec库,但有几个关于,也在VBA和其他HGL中...但是要注意一个完整的ArbPrec机器是一件非常重要的事情...幸运的是你的问题是这么简单。