在SML中如何将实数四舍五入到第n个小数点?

在SML中如何将实数四舍五入到第n个小数点?,sml,Sml,SML新手,尝试通过声明函数舍入(n,L)将实数舍入到第n个小数点,其中L是实数列表,n决定可以舍入到的第n个小数点 我的方法是先将实数转换为字符串,然后将子字符串转换为第n个小数点,然后将子字符串解析回实数,如果我只想将实数转换为第n个小数点,这很好,但是如果我有一个像0.3456这样的数字,我想将其四舍五入为0.35,我的方法就无法真正实现这一点 fun rd(_,[]) = [] |rd(a:int, x::y:real list) = if x>0.0 then Option.ge

SML新手,尝试通过声明函数舍入(n,L)将实数舍入到第n个小数点,其中L是实数列表,n决定可以舍入到的第n个小数点

我的方法是先将实数转换为字符串,然后将子字符串转换为第n个小数点,然后将子字符串解析回实数,如果我只想将实数转换为第n个小数点,这很好,但是如果我有一个像0.3456这样的数字,我想将其四舍五入为0.35,我的方法就无法真正实现这一点

fun rd(_,[]) = []
|rd(a:int, x::y:real list) =
if x>0.0
then Option.getOpt(Real.fromString(String.substring(Real.toString(x),0,a+2)),0.0) :: rd(a,y)
else Option.getOpt(Real.fromString(String.substring(Real.toString(x),0,a+3)),0.0) :: rd(a,y)
预期结果如下:

- rd (2, [0.1234, 0.2345, ~0.3456]);
val it = [0.12,0.23,~0.35] : real list`
但我得到的实际输出是

val it = [0.12,0.23,~0.34] : real list
如果我想把数字四舍五入,有什么好办法吗

我也试过:

fun rd(_,[]) = []
|rd(a:int, x::y:real list) =
let
val n = real(round(x*Math.pow(10.0,real(a)))) / Math.pow(10.0,real(a))
in n::rd(a,y)
end;
但是这个解决方案会给我一个未捕获的异常溢出

试图将实数四舍五入到第n个小数点

声明一个函数
四舍五入(n,L)
,其中L是实数列表,n决定第n个小数点

从您在第二次尝试解决方案中使用的
Math.pow(10.0,real(a))
判断,您似乎已经走上了正轨。我不明白清单是怎么来的;正如Yawar所指出的,尝试并解决单个实数的舍入问题,然后递归地(使用
map
)将其应用于实数列表

这是一个函数

fun roundN (x, n) = ...

fun roundManyN (xs, n) = map (fn x => roundN (x, n)) xs
首先制作一些示例,并将它们编码为测试。由于您在这些测试中失败,请首先创建(或复制)自定义相等运算符

fun nearlyEqual (a, b, eps) =
    let val absA = Real.abs a
        val absB = Real.abs b
        val diff = Real.abs (a - b)
    in Real.== (a, b) orelse
     ( if Real.== (a, 0.0) orelse
          Real.== (b, 0.0) orelse
          diff < Real.minNormalPos
       then diff < eps * Real.minNormalPos
       else diff / Real.min (absA + absB, Real.maxFinite) < eps )
    end

val test_roundN_1 =
  let val got = roundN (3.14159, 1)
      val expected = 3.1
  in nearlyEqual (got, expected, 0.1) end

val test_roundN_2 =
  let val got = roundN (3.14159, 2)
      val expected = 3.14
  in nearlyEqual (got, expected, 0.01) end

(* rounding point *)
val test_roundN_3 =
  let val got = roundN (3.14159, 3)
      val expected = 3.142
  in nearlyEqual (got, expected, 0.001) end

(* rounding point *)
val test_roundN_4 =
  let val got = roundN (3.14159, 4)
      val expected = 3.1416
  in nearlyEqual (got, expected, 0.0001) end

val test_roundN_5 =
  let val got = roundN (3.14159, 5)
      val expected = 3.14159
  in nearlyEqual (got, expected, 0.00001) end
此解决方案将给我一个未捕获的异常溢出

关于什么输入,我可能会问

一个来源可能是
round:real->int
是一个部分函数:有一些实值不能用int表示,例如
real.posInf
real.negInf
1e10
(在32位SML上)和
1e19
(在64位SML上)。为了避免这一点,请考虑使用<代码> Real.RealSn:Realth->Reals以避免int转换。


一种避免与
x*Math.pow(10.0,实数n)
相关的错误的方法是,在乘法之前去掉整数部分,在除法之后再加上整数部分。

基本库具有舍入函数,你可以用其中一个来做你需要的事情:-)我试过了,但它一直给我一个溢出异常…为什么你希望
String.substring(“~0.3456”)、0,5)
产生
“~0.35”
?@fishwolfs,这是因为递归而不是因为舍入。我建议先编写一个小数舍入函数,它可以处理单个数字。然后只需使用
List.map
将其应用于real列表。
fun roundN (x, n) =
    let val m = Math.pow(10.0, real n)
    in real (round (x * m)) / m end