bugfix: floating point precision; added strutils.formatFloat

This commit is contained in:
Araq
2011-01-09 14:52:15 +01:00
parent 8c799da867
commit 064417fc5a
13 changed files with 156 additions and 56 deletions

View File

@@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -24,6 +24,17 @@ const
PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number)
E* = 2.71828182845904523536028747 ## Euler's number
MaxFloat64Precision* = 16 ## maximum number of meaningful digits
## after the decimal point for Nimrod's
## ``float64`` type.
MaxFloat32Precision* = 8 ## maximum number of meaningful digits
## after the decimal point for Nimrod's
## ``float32`` type.
MaxFloatPrecision* = MaxFloat64Precision ## maximum number of
## meaningful digits
## after the decimal point
## for Nimrod's ``float`` type.
type
TFloatClass* = enum ## describes the class a floating point value belongs to.
## This is the type that is returned by `classify`.

View File

@@ -935,6 +935,60 @@ proc editDistance*(a, b: string): int {.noSideEffect,
row[p] = x
result = row[e]
#dealloc(row)
# floating point formating:
proc c_sprintf(buf, frmt: CString) {.nodecl, importc: "sprintf", varargs,
noSideEffect.}
type
TFloatFormat* = enum
ffDefault, ## use the shorter floating point notation
ffDecimal, ## use decimal floating point notation
ffScientific ## use scientific notation (using ``e``) character
proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault,
precision = 16): string {.noSideEffect,
rtl, extern: "nsu$1".} =
## converts a floating point value `f` to a string.
##
## If ``format == ffDecimal`` then precision is the number of digits to
## be printed after the decimal point.
## If ``format == ffScientific`` then precision is the maximum number
## of significant digits to be printed.
## `precision`'s default value is the maximum number of meaningful digits
## after the decimal point for Nimrod's ``biggestFloat`` type.
const floatFormatToChar: array[TFloatFormat, char] = ['g', 'f', 'e']
var
frmtstr: array[0..5, char]
buf: array[0..80, char]
frmtstr[0] = '%'
frmtstr[1] = '#'
if precision > 0:
frmtstr[2] = '.'
frmtstr[3] = '*'
frmtstr[4] = floatFormatToChar[format]
frmtstr[5] = '\0'
c_sprintf(buf, frmtstr, precision, f)
else:
frmtstr[2] = floatFormatToChar[format]
frmtstr[3] = '\0'
c_sprintf(buf, frmtstr, f)
result = $buf
proc formatFloat*(f: float, format: TFloatFormat = ffDefault,
precision = 16): string {.noSideEffect,
rtl, extern: "nsu$1".} =
## converts a floating point value `f` to a string.
##
## If ``format == ffDecimal`` then precision is the number of digits to
## be printed after the decimal point.
## If ``format == ffScientific`` then precision is the maximum number
## of significant digits to be printed.
## `precision`'s default value is the maximum number of meaningful digits
## after the decimal point for Nimrod's ``float`` type.
result = formatBiggestFloat(f, format, precision)
{.pop.}
@@ -944,5 +998,7 @@ when isMainModule:
assert align("1232", 6) == " 1232"
echo wordWrap(""" this is a long text -- muchlongerthan10chars and here
it goes""", 10, false)
assert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
assert formatBiggestFloat(0.00000000001, ffScientific, 1) == "1.0e-11"

View File

@@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2009 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -34,7 +34,8 @@ var
c_stdout {.importc: "stdout", noDecl.}: C_TextFileStar
c_stderr {.importc: "stderr", noDecl.}: C_TextFileStar
var # constants faked as variables:
# constants faked as variables:
var
SIGINT {.importc: "SIGINT", nodecl.}: cint
SIGSEGV {.importc: "SIGSEGV", nodecl.}: cint
SIGABRT {.importc: "SIGABRT", nodecl.}: cint

View File

@@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.

View File

@@ -12,7 +12,7 @@
# the data structures here are used in various places of the compiler.
import
ast, nhashes, strutils, options, msgs, ropes, idents
ast, nhashes, strutils, options, msgs, ropes, idents, rodutils
proc hashNode*(p: PObject): THash
proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope
@@ -327,7 +327,8 @@ proc treeToYamlAux(n: PNode, marker: var TIntSet, indent: int,
of nkCharLit..nkInt64Lit:
appf(result, ",$n$1\"intVal\": $2", [istr, toRope(n.intVal)])
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
appf(result, ",$n$1\"floatVal\": $2", [istr, toRopeF(n.floatVal)])
appf(result, ",$n$1\"floatVal\": $2",
[istr, toRope(n.floatVal.ToStrMaxPrecision)])
of nkStrLit..nkTripleStrLit:
appf(result, ",$n$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym:
@@ -396,7 +397,8 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int): PRope =
of nkCharLit..nkInt64Lit:
appf(result, ",$n$1\"intVal\": $2", [istr, toRope(n.intVal)])
of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
appf(result, ",$n$1\"floatVal\": $2", [istr, toRopeF(n.floatVal)])
appf(result, ",$n$1\"floatVal\": $2",
[istr, toRope(n.floatVal.ToStrMaxPrecision)])
of nkStrLit..nkTripleStrLit:
appf(result, ",$n$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym:
@@ -430,12 +432,13 @@ proc debug(n: PNode) =
writeln(stdout, ropeToStr(debugTree(n, 0, 100)))
const
EmptySeq = @ []
EmptySeq = @[]
proc nextTry(h, maxHash: THash): THash =
result = ((5 * h) + 1) and maxHash # For any initial h in range(maxHash), repeating that maxHash times
# generates each int in range(maxHash) exactly once (see any text on
# random-number generation for proof).
result = ((5 * h) + 1) and maxHash
# For any initial h in range(maxHash), repeating that maxHash times
# generates each int in range(maxHash) exactly once (see any text on
# random-number generation for proof).
proc objectSetContains(t: TObjectSet, obj: PObject): bool =
# returns true whether n is in t
@@ -498,7 +501,8 @@ proc TableRawGet(t: TTable, key: PObject): int =
h = nextTry(h, high(t.data))
result = - 1
proc TableSearch(t: TTable, key, closure: PObject, comparator: TCmpProc): PObject =
proc TableSearch(t: TTable, key, closure: PObject,
comparator: TCmpProc): PObject =
var h: THash
h = hashNode(key) and high(t.data) # start with real hash value
while t.data[h].key != nil:
@@ -510,8 +514,7 @@ proc TableSearch(t: TTable, key, closure: PObject, comparator: TCmpProc): PObjec
result = nil
proc TableGet(t: TTable, key: PObject): PObject =
var index: int
index = TableRawGet(t, key)
var index = TableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
@@ -533,8 +536,7 @@ proc TableEnlarge(t: var TTable) =
swap(t.data, n)
proc TablePut(t: var TTable, key, val: PObject) =
var index: int
index = TableRawGet(t, key)
var index = TableRawGet(t, key)
if index >= 0:
t.data[index].val = val
else:
@@ -692,20 +694,17 @@ proc IdTableRawGet(t: TIdTable, key: int): int =
result = - 1
proc IdTableHasObjectAsKey(t: TIdTable, key: PIdObj): bool =
var index: int
index = IdTableRawGet(t, key.id)
var index = IdTableRawGet(t, key.id)
if index >= 0: result = t.data[index].key == key
else: result = false
proc IdTableGet(t: TIdTable, key: PIdObj): PObject =
var index: int
index = IdTableRawGet(t, key.id)
var index = IdTableRawGet(t, key.id)
if index >= 0: result = t.data[index].val
else: result = nil
proc IdTableGet(t: TIdTable, key: int): PObject =
var index: int
index = IdTableRawGet(t, key)
var index = IdTableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
@@ -797,8 +796,7 @@ proc IITableRawGet(t: TIITable, key: int): int =
result = - 1
proc IITableGet(t: TIITable, key: int): int =
var index: int
index = IITableRawGet(t, key)
var index = IITableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = InvalidKey
@@ -813,15 +811,13 @@ proc IITableRawInsert(data: var TIIPairSeq, key, val: int) =
data[h].val = val
proc IITablePut(t: var TIITable, key, val: int) =
var
index: int
n: TIIPairSeq
index = IITableRawGet(t, key)
var index = IITableRawGet(t, key)
if index >= 0:
assert(t.data[index].key != InvalidKey)
t.data[index].val = val
else:
if mustRehash(len(t.data), t.counter):
var n: TIIPairSeq
newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(n)): n[i].key = InvalidKey
for i in countup(0, high(t.data)):

View File

@@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2010 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -12,7 +12,8 @@
proc intLiteral(i: biggestInt): PRope =
if (i > low(int32)) and (i <= high(int32)):
result = toRope(i)
elif i == low(int32): # Nimrod has the same bug for the same reasons :-)
elif i == low(int32):
# Nimrod has the same bug for the same reasons :-)
result = toRope("(-2147483647 -1)")
elif i > low(int64):
result = ropef("IL64($1)", [toRope(i)])
@@ -76,16 +77,7 @@ proc genLiteral(p: BProc, v: PNode, ty: PType): PRope =
else:
result = makeCString(v.strVal)
of nkFloatLit..nkFloat64Lit:
var f = v.floatVal
if f != f:
result = toRope("NAN")
elif f == 0.0:
result = toRopeF(f)
elif f == 0.5 * f:
if f > 0.0: result = toRope("INF")
else: result = toRope("-INF")
else:
result = toRopeF(f)
result = toRope(v.floatVal.ToStrMaxPrecision)
else:
InternalError(v.info, "genLiteral(" & $v.kind & ')')
result = nil

View File

@@ -13,7 +13,8 @@
import
ast, astalgo, strutils, nhashes, trees, platform, magicsys, extccomp, options,
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
times, ropes, math, passes, rodread, wordrecg, rnimsyn, treetab, cgmeth
times, ropes, math, passes, rodread, wordrecg, rnimsyn, treetab, cgmeth,
rodutils
when options.hasTinyCBackend:
import tccgen

View File

@@ -14,7 +14,7 @@
import
ast, astalgo, strutils, nhashes, trees, platform, magicsys, extccomp,
options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os,
times, ropes, math, passes, ccgutils, wordrecg, rnimsyn, rodread
times, ropes, math, passes, ccgutils, wordrecg, rnimsyn, rodread, rodutils
proc ecmasgenPass*(): TPass
# implementation
@@ -115,7 +115,7 @@ proc mapType(typ: PType): TEcmasTypeKind =
proc mangle(name: string): string =
result = ""
for i in countup(0, len(name) + 0 - 1):
for i in countup(0, len(name) - 1):
case name[i]
of 'A'..'Z':
add(result, chr(ord(name[i]) - ord('A') + ord('a')))
@@ -1357,11 +1357,11 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) =
of nkFloatLit..nkFloat64Lit:
f = n.floatVal
if f != f: r.res = toRope("NaN")
elif f == 0.0: r.res = toRopeF(f)
elif f == 0.0: r.res = toRope("0.0")
elif f == 0.5 * f:
if f > 0.0: r.res = toRope("Infinity")
else: r.res = toRope("-Infinity")
else: r.res = toRopeF(f)
else: r.res = toRope(f.ToStrMaxPrecision)
of nkBlockExpr: genBlock(p, n, r)
of nkIfExpr: genIfExpr(p, n, r)
of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit:

27
rod/rodutils.nim Normal file
View File

@@ -0,0 +1,27 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Utilities for the compiler. Aim is to reduce the coupling between
## the compiler and the evolving stdlib.
proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", nodecl, varargs.}
proc ToStrMaxPrecision*(f: BiggestFloat): string =
if f != f:
result = "NAN"
elif f == 0.0:
result = "0.0"
elif f == 0.5 * f:
if f > 0.0: result = "INF"
else: result = "-INF"
else:
var buf: array [0..80, char]
c_sprintf(buf, "%#.16e", f)
result = $buf

View File

@@ -87,7 +87,6 @@ proc app*(a: var PRope, b: PRope)
proc app*(a: var PRope, b: string)
proc prepend*(a: var PRope, b: PRope)
proc toRope*(s: string): PRope
proc toRopeF*(r: BiggestFloat): PRope
proc toRope*(i: BiggestInt): PRope
proc ropeLen*(a: PRope): int
proc WriteRope*(head: PRope, filename: string)
@@ -270,7 +269,7 @@ proc con(a: openarray[PRope]): PRope =
for i in countup(0, high(a)): result = con(result, a[i])
proc toRope(i: BiggestInt): PRope = result = toRope($i)
proc toRopeF(r: BiggestFloat): PRope = result = toRope($r)
#proc toRopeF*(r: BiggestFloat): PRope = result = toRope($r)
proc app(a: var PRope, b: PRope) = a = con(a, b)
proc app(a: var PRope, b: string) = a = con(a, b)
proc prepend(a: var PRope, b: PRope) = a = con(b, a)
@@ -303,11 +302,10 @@ proc WriteRope(head: PRope, filename: string) =
rawMessage(errCannotOpenFile, filename)
proc ropef(frmt: TFormatStr, args: openarray[PRope]): PRope =
var i, j, length, start, num: int
i = 0
length = len(frmt)
var i = 0
var length = len(frmt)
result = nil
num = 0
var num = 0
while i <= length - 1:
if frmt[i] == '$':
inc(i) # skip '$'
@@ -320,7 +318,7 @@ proc ropef(frmt: TFormatStr, args: openarray[PRope]): PRope =
app(result, args[num])
inc(num)
of '0'..'9':
j = 0
var j = 0
while true:
j = (j * 10) + Ord(frmt[i]) - ord('0')
inc(i)
@@ -333,7 +331,7 @@ proc ropef(frmt: TFormatStr, args: openarray[PRope]): PRope =
app(result, tnl)
inc(i)
else: InternalError("ropes: invalid format string $" & frmt[i])
start = i
var start = i
while (i <= length - 1):
if (frmt[i] != '$'): inc(i)
else: break

View File

@@ -27,6 +27,7 @@ tfinally2.nim;ABCD
tfinally3.nim;false
tfloat1.nim;Error: unhandled exception: FPU operation caused an overflow [EFloatOverflow]
tfloat2.nim;Error: unhandled exception: FPU operation caused a NaN result [EFloatInvalidOp]
tfloat3.nim;Nimrod 3.4368930843, 0.3299290698 C double: 3.4368930843, 0.3299290698
tformat.nim;Hi Andreas! How do you feel, Rumpf?
thintoff.nim;0
tinit.nim;Hello from module! Hello from main module!
1 tack.nim 125
27 tfinally3.nim false
28 tfloat1.nim Error: unhandled exception: FPU operation caused an overflow [EFloatOverflow]
29 tfloat2.nim Error: unhandled exception: FPU operation caused a NaN result [EFloatInvalidOp]
30 tfloat3.nim Nimrod 3.4368930843, 0.3299290698 C double: 3.4368930843, 0.3299290698
31 tformat.nim Hi Andreas! How do you feel, Rumpf?
32 thintoff.nim 0
33 tinit.nim Hello from module! Hello from main module!

View File

@@ -0,0 +1,18 @@
import math, strutils
{.emit: """
void printFloats(void) {
double y = 1.234567890123456789;
printf("C double: %.10f, %.10f ", exp(y), cos(y));
}
""".}
proc c_printf(frmt: CString) {.importc: "printf", header: "<stdio.h>", varargs.}
proc printFloats {.importc, nodecl.}
var x: float = 1.234567890123456789
c_printf("Nimrod %.10f, %.10f ", exp(x), cos(x))
printFloats()

View File

@@ -59,7 +59,6 @@ Low priority
Library
-------
- float formatting
- locale support
- conversion between character sets
- bignums