bugfix: floating point precision; added strutils.formatFloat

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

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2011 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -24,6 +24,17 @@ const
PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number) PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number)
E* = 2.71828182845904523536028747 ## Euler'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 type
TFloatClass* = enum ## describes the class a floating point value belongs to. TFloatClass* = enum ## describes the class a floating point value belongs to.
## This is the type that is returned by `classify`. ## This is the type that is returned by `classify`.

View file

@ -936,6 +936,60 @@ proc editDistance*(a, b: string): int {.noSideEffect,
result = row[e] result = row[e]
#dealloc(row) #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.} {.pop.}
when isMainModule: when isMainModule:
@ -944,5 +998,7 @@ when isMainModule:
assert align("1232", 6) == " 1232" assert align("1232", 6) == " 1232"
echo wordWrap(""" this is a long text -- muchlongerthan10chars and here echo wordWrap(""" this is a long text -- muchlongerthan10chars and here
it goes""", 10, false) 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 # Nimrod's Runtime Library
# (c) Copyright 2009 Andreas Rumpf # (c) Copyright 2011 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -34,7 +34,8 @@ var
c_stdout {.importc: "stdout", noDecl.}: C_TextFileStar c_stdout {.importc: "stdout", noDecl.}: C_TextFileStar
c_stderr {.importc: "stderr", 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 SIGINT {.importc: "SIGINT", nodecl.}: cint
SIGSEGV {.importc: "SIGSEGV", nodecl.}: cint SIGSEGV {.importc: "SIGSEGV", nodecl.}: cint
SIGABRT {.importc: "SIGABRT", nodecl.}: cint SIGABRT {.importc: "SIGABRT", nodecl.}: cint

View file

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

View file

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

View file

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

View file

@ -13,7 +13,8 @@
import import
ast, astalgo, strutils, nhashes, trees, platform, magicsys, extccomp, options, ast, astalgo, strutils, nhashes, trees, platform, magicsys, extccomp, options,
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os, 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: when options.hasTinyCBackend:
import tccgen import tccgen

View file

@ -14,7 +14,7 @@
import import
ast, astalgo, strutils, nhashes, trees, platform, magicsys, extccomp, ast, astalgo, strutils, nhashes, trees, platform, magicsys, extccomp,
options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os, 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 proc ecmasgenPass*(): TPass
# implementation # implementation
@ -115,7 +115,7 @@ proc mapType(typ: PType): TEcmasTypeKind =
proc mangle(name: string): string = proc mangle(name: string): string =
result = "" result = ""
for i in countup(0, len(name) + 0 - 1): for i in countup(0, len(name) - 1):
case name[i] case name[i]
of 'A'..'Z': of 'A'..'Z':
add(result, chr(ord(name[i]) - ord('A') + ord('a'))) 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: of nkFloatLit..nkFloat64Lit:
f = n.floatVal f = n.floatVal
if f != f: r.res = toRope("NaN") 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: elif f == 0.5 * f:
if f > 0.0: r.res = toRope("Infinity") if f > 0.0: r.res = toRope("Infinity")
else: 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 nkBlockExpr: genBlock(p, n, r)
of nkIfExpr: genIfExpr(p, n, r) of nkIfExpr: genIfExpr(p, n, r)
of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit: 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 app*(a: var PRope, b: string)
proc prepend*(a: var PRope, b: PRope) proc prepend*(a: var PRope, b: PRope)
proc toRope*(s: string): PRope proc toRope*(s: string): PRope
proc toRopeF*(r: BiggestFloat): PRope
proc toRope*(i: BiggestInt): PRope proc toRope*(i: BiggestInt): PRope
proc ropeLen*(a: PRope): int proc ropeLen*(a: PRope): int
proc WriteRope*(head: PRope, filename: string) 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]) for i in countup(0, high(a)): result = con(result, a[i])
proc toRope(i: BiggestInt): PRope = result = toRope($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: PRope) = a = con(a, b)
proc app(a: var PRope, b: string) = 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) proc prepend(a: var PRope, b: PRope) = a = con(b, a)
@ -303,11 +302,10 @@ proc WriteRope(head: PRope, filename: string) =
rawMessage(errCannotOpenFile, filename) rawMessage(errCannotOpenFile, filename)
proc ropef(frmt: TFormatStr, args: openarray[PRope]): PRope = proc ropef(frmt: TFormatStr, args: openarray[PRope]): PRope =
var i, j, length, start, num: int var i = 0
i = 0 var length = len(frmt)
length = len(frmt)
result = nil result = nil
num = 0 var num = 0
while i <= length - 1: while i <= length - 1:
if frmt[i] == '$': if frmt[i] == '$':
inc(i) # skip '$' inc(i) # skip '$'
@ -320,7 +318,7 @@ proc ropef(frmt: TFormatStr, args: openarray[PRope]): PRope =
app(result, args[num]) app(result, args[num])
inc(num) inc(num)
of '0'..'9': of '0'..'9':
j = 0 var j = 0
while true: while true:
j = (j * 10) + Ord(frmt[i]) - ord('0') j = (j * 10) + Ord(frmt[i]) - ord('0')
inc(i) inc(i)
@ -333,7 +331,7 @@ proc ropef(frmt: TFormatStr, args: openarray[PRope]): PRope =
app(result, tnl) app(result, tnl)
inc(i) inc(i)
else: InternalError("ropes: invalid format string $" & frmt[i]) else: InternalError("ropes: invalid format string $" & frmt[i])
start = i var start = i
while (i <= length - 1): while (i <= length - 1):
if (frmt[i] != '$'): inc(i) if (frmt[i] != '$'): inc(i)
else: break else: break

View file

@ -27,6 +27,7 @@ tfinally2.nim;ABCD
tfinally3.nim;false tfinally3.nim;false
tfloat1.nim;Error: unhandled exception: FPU operation caused an overflow [EFloatOverflow] tfloat1.nim;Error: unhandled exception: FPU operation caused an overflow [EFloatOverflow]
tfloat2.nim;Error: unhandled exception: FPU operation caused a NaN result [EFloatInvalidOp] 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? tformat.nim;Hi Andreas! How do you feel, Rumpf?
thintoff.nim;0 thintoff.nim;0
tinit.nim;Hello from module! Hello from main module! 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 Library
------- -------
- float formatting
- locale support - locale support
- conversion between character sets - conversion between character sets
- bignums - bignums