mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-06 11:54:11 +00:00
bugfix: floating point precision; added strutils.formatFloat
This commit is contained in:
@@ -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`.
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
27
rod/rodutils.nim
Normal 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!
|
||||
|
||||
|
18
tests/accept/run/tfloat3.nim
Normal file
18
tests/accept/run/tfloat3.nim
Normal 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()
|
||||
|
||||
Reference in New Issue
Block a user