trying to round up a real number to nth decimal
declaring a function round(n,L)
, where L is a list of real numbers and n decide the nth decimal
Judging by your use of Math.pow(10.0,real(a))
in your second attempted solution, you seem to be on track. I don't understand where a list comes in; as Yawar points out, try and solve this for rounding a single real, and then apply that recursively (using map
) to a list of reals.
So a function
fun roundN (x, n) = ...
fun roundManyN (xs, n) = map (fn x => roundN (x, n)) xs
Start by making some examples and encode them as tests. Since you can't compare real for equality in those tests, start by making (or copying) a custom equality operator.
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
You also have some edge cases that you eventually want to deal with:
- When
n
is zero or negative, or when n
is greater than the number of digits in the fraction.
- When
x
is close to a rounding point, e.g. roundN (3.1451, 2)
~> 3.15
.
- When
x·10ⁿ
has a magnitude that exceeds the size of an int.
- When
n
is so large that a magnitude change may affect the precision of a real.
For a better testing library, check out testlib.sml (and its use in test.sml) in this exercism exercise.
Extracting your second solution into a function, and giving Math.pow (10.0, real n)
a temporary binding, you get the solution:
fun roundN (x, n) =
let val m = Math.pow(10.0, real n)
in real (round (x * m)) / m end
this solution will give me an uncaught exception overflow
On what input, I might ask.
One source could be that round : real -> int
is a partial function: There are real values that cannot be expressed as int, such as Real.posInf
, Real.negInf
, 1e10
(on 32-bit SML) and 1e19
(on 64-bit SML). To avoid this, consider using Real.realRound : real -> real
to avoid the int conversion.
One way to avoid errors related to x * Math.pow(10.0, real n)
causing imprecision because the number grows too big, could be to strip the integer part before multiplying, and adding the integer part back after dividing.
String.substring("~0.3456"),0,5)
to produce"~0.35"
? – molbdniloList.map
to apply it to a list of reals. – Yawar