Merge pull request #4245 from abudden/new-formateng

Modification to implementation of round() such that it returns a floa…
This commit is contained in:
Andreas Rumpf 2016-05-31 20:23:50 +02:00
commit 80f2e3ea00
4 changed files with 101 additions and 27 deletions

View file

@ -489,7 +489,7 @@ proc leValueConv(a, b: PNode): bool =
of nkCharLit..nkUInt64Lit: of nkCharLit..nkUInt64Lit:
case b.kind case b.kind
of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal
of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal) of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal).int
else: internalError(a.info, "leValueConv") else: internalError(a.info, "leValueConv")
of nkFloatLit..nkFloat128Lit: of nkFloatLit..nkFloat128Lit:
case b.kind case b.kind

View file

@ -138,19 +138,10 @@ when not defined(JS):
proc exp*(x: float64): float64 {.importc: "exp", header: "<math.h>".} proc exp*(x: float64): float64 {.importc: "exp", header: "<math.h>".}
## Computes the exponential function of `x` (pow(E, x)) ## Computes the exponential function of `x` (pow(E, x))
proc frexp*(x: float32, exponent: var int): float32 {. proc round0(x: float32): float32 {.importc: "roundf", header: "<math.h>".}
importc: "frexp", header: "<math.h>".} proc round0(x: float64): float64 {.importc: "round", header: "<math.h>".}
proc frexp*(x: float64, exponent: var int): float64 {. ## Converts a float to an int by rounding. Used internally by the round
importc: "frexp", header: "<math.h>".} ## function when the specified number of places is 0.
## Split a number into mantissa and exponent.
## `frexp` calculates the mantissa m (a float greater than or equal to 0.5
## and less than 1) and the integer value n such that `x` (the original
## float value) equals m * 2**n. frexp stores n in `exponent` and returns
## m.
proc round*(x: float32): int {.importc: "lrintf", header: "<math.h>".}
proc round*(x: float64): int {.importc: "lrint", header: "<math.h>".}
## converts a float to an int by rounding.
proc arccos*(x: float32): float32 {.importc: "acosf", header: "<math.h>".} proc arccos*(x: float32): float32 {.importc: "acosf", header: "<math.h>".}
proc arccos*(x: float64): float64 {.importc: "acos", header: "<math.h>".} proc arccos*(x: float64): float64 {.importc: "acos", header: "<math.h>".}
@ -256,22 +247,11 @@ else:
proc exp*(x: float32): float32 {.importc: "Math.exp", nodecl.} proc exp*(x: float32): float32 {.importc: "Math.exp", nodecl.}
proc exp*(x: float64): float64 {.importc: "Math.exp", nodecl.} proc exp*(x: float64): float64 {.importc: "Math.exp", nodecl.}
proc round*(x: float): int {.importc: "Math.round", nodecl.} proc round0(x: float): float {.importc: "Math.round", nodecl.}
proc pow*(x, y: float32): float32 {.importC: "Math.pow", nodecl.} proc pow*(x, y: float32): float32 {.importC: "Math.pow", nodecl.}
proc pow*(x, y: float64): float64 {.importc: "Math.pow", nodecl.} proc pow*(x, y: float64): float64 {.importc: "Math.pow", nodecl.}
proc frexp*[T: float32|float64](x: T, exponent: var int): T =
if x == 0.0:
exponent = 0
result = 0.0
elif x < 0.0:
result = -frexp(-x, exponent)
else:
var ex = floor(log2(x))
exponent = round(ex)
result = x / pow(2.0, ex)
proc arccos*(x: float32): float32 {.importc: "Math.acos", nodecl.} proc arccos*(x: float32): float32 {.importc: "Math.acos", nodecl.}
proc arccos*(x: float64): float64 {.importc: "Math.acos", nodecl.} proc arccos*(x: float64): float64 {.importc: "Math.acos", nodecl.}
proc arcsin*(x: float32): float32 {.importc: "Math.asin", nodecl.} proc arcsin*(x: float32): float32 {.importc: "Math.asin", nodecl.}
@ -295,6 +275,60 @@ else:
var y = exp(2.0*x) var y = exp(2.0*x)
return (y-1.0)/(y+1.0) return (y-1.0)/(y+1.0)
proc round*[T: float32|float64](x: T, places: int = 0): T =
## Round a floating point number.
##
## If `places` is 0 (or omitted), round to the nearest integral value
## following normal mathematical rounding rules (e.g. `round(54.5) -> 55.0`).
## If `places` is greater than 0, round to the given number of decimal
## places, e.g. `round(54.346, 2) -> 54.35`.
## If `places` is negative, round to the left of the decimal place, e.g.
## `round(537.345, -1) -> 540.0`
if places == 0:
result = round0(x)
else:
var mult = pow(10.0, places.T)
result = round0(x*mult)/mult
when not defined(JS):
proc frexp*(x: float32, exponent: var int): float32 {.
importc: "frexp", header: "<math.h>".}
proc frexp*(x: float64, exponent: var int): float64 {.
importc: "frexp", header: "<math.h>".}
## Split a number into mantissa and exponent.
## `frexp` calculates the mantissa m (a float greater than or equal to 0.5
## and less than 1) and the integer value n such that `x` (the original
## float value) equals m * 2**n. frexp stores n in `exponent` and returns
## m.
else:
proc frexp*[T: float32|float64](x: T, exponent: var int): T =
if x == 0.0:
exponent = 0
result = 0.0
elif x < 0.0:
result = -frexp(-x, exponent)
else:
var ex = floor(log2(x))
exponent = round(ex)
result = x / pow(2.0, ex)
proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
## Breaks `x` into an integral and a fractional part.
##
## Returns a tuple containing intpart and floatpart representing
## the integer part and the fractional part respectively.
##
## Both parts have the same sign as `x`. Analogous to the `modf`
## function in C.
var
absolute: T
absolute = abs(x)
result.intpart = floor(absolute)
result.floatpart = absolute - result.intpart
if x < 0:
result.intpart = -result.intpart
result.floatpart = -result.floatpart
{.pop.} {.pop.}
proc degToRad*[T: float32|float64](d: T): T {.inline.} = proc degToRad*[T: float32|float64](d: T): T {.inline.} =
@ -357,3 +391,36 @@ when isMainModule and not defined(JS):
assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0 assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0
assert(erf(6.0) > erf(5.0)) assert(erf(6.0) > erf(5.0))
assert(erfc(6.0) < erfc(5.0)) assert(erfc(6.0) < erfc(5.0))
when isMainModule:
# Function for approximate comparison of floats
proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9)
block: # round() tests
# Round to 0 decimal places
doAssert round(54.652) ==~ 55.0
doAssert round(54.352) ==~ 54.0
doAssert round(-54.652) ==~ -55.0
doAssert round(-54.352) ==~ -54.0
doAssert round(0.0) ==~ 0.0
# Round to positive decimal places
doAssert round(-547.652, 1) ==~ -547.7
doAssert round(547.652, 1) ==~ 547.7
doAssert round(-547.652, 2) ==~ -547.65
doAssert round(547.652, 2) ==~ 547.65
# Round to negative decimal places
doAssert round(547.652, -1) ==~ 550.0
doAssert round(547.652, -2) ==~ 500.0
doAssert round(547.652, -3) ==~ 1000.0
doAssert round(547.652, -4) ==~ 0.0
doAssert round(-547.652, -1) ==~ -550.0
doAssert round(-547.652, -2) ==~ -500.0
doAssert round(-547.652, -3) ==~ -1000.0
doAssert round(-547.652, -4) ==~ 0.0
block: # splitDecimal() tests
doAssert splitDecimal(54.674).intpart ==~ 54.0
doAssert splitDecimal(54.674).floatpart ==~ 0.674
doAssert splitDecimal(-693.4356).intpart ==~ -693.0
doAssert splitDecimal(-693.4356).floatpart ==~ -0.4356
doAssert splitDecimal(0.0).intpart ==~ 0.0
doAssert splitDecimal(0.0).floatpart ==~ 0.0

View file

@ -10,7 +10,7 @@ template test(loopCount: int, extraI: int, testBody: stmt): stmt =
template test(loopCount: int, extraF: float, testBody: stmt): stmt = template test(loopCount: int, extraF: float, testBody: stmt): stmt =
block: block:
test(loopCount, round(extraF), testBody) test(loopCount, round(extraF).int, testBody)
template test(loopCount: int, testBody: stmt): stmt = template test(loopCount: int, testBody: stmt): stmt =
block: block:

View file

@ -45,6 +45,11 @@ Changes affecting backwards compatibility
- The path handling changed. The project directory is not added to the - The path handling changed. The project directory is not added to the
search path automatically anymore. Add this line to your project's search path automatically anymore. Add this line to your project's
config to get back the old behaviour: ``--path:"$projectdir"``. config to get back the old behaviour: ``--path:"$projectdir"``.
- The ``round`` function in ``math.nim`` now returns a float and has been
corrected such that the C implementation always rounds up from .5 rather
than changing the operation for even and odd numbers.
- The ``round`` function now accepts a ``places`` argument to round to a
given number of places (e.g. round 4.35 to 4.4 if ``places`` is 1).
Library Additions Library Additions
@ -56,6 +61,8 @@ Library Additions
- Added ``strscans`` module that implements a ``scanf`` for easy input extraction. - Added ``strscans`` module that implements a ``scanf`` for easy input extraction.
- Added a version of ``parseutils.parseUntil`` that can deal with a string ``until`` token. The other - Added a version of ``parseutils.parseUntil`` that can deal with a string ``until`` token. The other
versions are for ``char`` and ``set[char]``. versions are for ``char`` and ``set[char]``.
- Added ``splitDecimal`` to ``math.nim`` to split a floating point value
into an integer part and a floating part (in the range -1<x<1).
Compiler Additions Compiler Additions