move formatfloat out of system (#20195)

* move formatfloat out of system

* fixes doc

* Update changelog.md

* careless

* fixes

* deprecate system/formatfloat

* better handling
This commit is contained in:
ringabout 2022-08-24 19:38:30 +08:00 • committed by GitHub
commit b6bfe38ff5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 201 additions and 176 deletions

View file

@ -6,8 +6,7 @@
- `addr` is now available for all addressable locations, - `addr` is now available for all addressable locations,
`unsafeAddr` is now deprecated and an alias for `addr`. `unsafeAddr` is now deprecated and an alias for `addr`.
- `io` and `assertions` are about to move out of the `system` module. - `io`, `assertions`, `formatfloat` are about to move out of the `system` module. You may instead import `std/syncio`, `std/assertions` and `std/formatfloat`.
You may instead import `std/syncio` and `std/assertions`.
The `-d:nimPreviewSlimSystem` option makes these imports required. The `-d:nimPreviewSlimSystem` option makes these imports required.
- The `gc:v2` option is removed. - The `gc:v2` option is removed.

View file

@ -15,7 +15,7 @@ import ".." / [ast, idents, lineinfos, msgs, ropes, options,
from os import removeFile, isAbsolute from os import removeFile, isAbsolute
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/[syncio, assertions] import std/[syncio, assertions, formatfloat]
type type
PackedConfig* = object PackedConfig* = object

View file

@ -20,7 +20,7 @@ import
wordrecg, lineinfos, pathutils, parseutils wordrecg, lineinfos, pathutils, parseutils
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/assertions import std/[assertions, formatfloat]
const const
MaxLineLength* = 80 # lines longer than this lead to a warning MaxLineLength* = 80 # lines longer than this lead to a warning

View file

@ -18,7 +18,7 @@ import
lexer, options, idents, strutils, ast, msgs, lineinfos lexer, options, idents, strutils, ast, msgs, lineinfos
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/[syncio, assertions] import std/[syncio, assertions, formatfloat]
type type
TRenderFlag* = enum TRenderFlag* = enum

View file

@ -42,7 +42,7 @@ when not declared(signbit):
proc signbit*(x: SomeFloat): bool {.inline.} = proc signbit*(x: SomeFloat): bool {.inline.} =
result = c_signbit(x) != 0 result = c_signbit(x) != 0
import system/formatfloat import std/formatfloat
proc toStrMaxPrecision*(f: BiggestFloat | float32): string = proc toStrMaxPrecision*(f: BiggestFloat | float32): string =
const literalPostfix = when f is float32: "f" else: "" const literalPostfix = when f is float32: "f" else: ""

View file

@ -61,7 +61,7 @@ import
from pathutils import AbsoluteFile from pathutils import AbsoluteFile
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/[assertions, syncio] import std/[assertions, syncio, formatfloat]
type type

View file

@ -25,6 +25,9 @@ when defined(nimfix):
when not defined(leanCompiler): when not defined(leanCompiler):
import spawn import spawn
when defined(nimPreviewSlimSystem):
import std/formatfloat
# implementation # implementation
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode

View file

@ -18,7 +18,7 @@ import
from system/memory import nimCStrLen from system/memory import nimCStrLen
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/assertions import std/[assertions, formatfloat]
proc errorType*(g: ModuleGraph): PType = proc errorType*(g: ModuleGraph): PType =
## creates a type representing an error state ## creates a type representing an error state

View file

@ -14,7 +14,7 @@ import
lineinfos, int128, modulegraphs, astmsgs lineinfos, int128, modulegraphs, astmsgs
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/assertions import std/[assertions, formatfloat]
type type
TPreferedDesc* = enum TPreferedDesc* = enum

View file

@ -18,6 +18,9 @@ import
gorgeimpl, lineinfos, btrees, macrocacheimpl, gorgeimpl, lineinfos, btrees, macrocacheimpl,
modulegraphs, sighashes, int128, vmprofiler modulegraphs, sighashes, int128, vmprofiler
when defined(nimPreviewSlimSystem):
import std/formatfloat
import ast except getstr import ast except getstr
from semfold import leValueConv, ordinalValToString from semfold import leValueConv, ordinalValToString
from evaltempl import evalTemplate from evaltempl import evalTemplate

View file

@ -13,7 +13,7 @@ import streams, json, intsets, tables, ast, astalgo, idents, types, msgs,
options, lineinfos options, lineinfos
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/assertions import std/[assertions, formatfloat]
proc ptrToInt(x: PNode): int {.inline.} = proc ptrToInt(x: PNode): int {.inline.} =
result = cast[int](x) # don't skip alignment result = cast[int](x) # don't skip alignment

View file

@ -28,12 +28,12 @@ from std/os import getEnv, existsEnv, delEnv, putEnv, envPairs,
from std/times import cpuTime from std/times import cpuTime
from std/hashes import hash from std/hashes import hash
from std/osproc import nil from std/osproc import nil
from system/formatfloat import addFloatRoundtrip, addFloatSprintf
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/syncio import std/syncio
else:
from std/formatfloat import addFloatRoundtrip, addFloatSprintf
# There are some useful procs in vmconv. # There are some useful procs in vmconv.
import vmconv, vmmarshal import vmconv, vmmarshal

View file

@ -11,7 +11,7 @@ include "system/inclrtl"
import std/private/since import std/private/since
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/assertions import std/[assertions, formatfloat]
## This module contains the interface to the compiler's abstract syntax ## This module contains the interface to the compiler's abstract syntax

View file

@ -44,7 +44,7 @@ import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils,
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/[assertions, syncio] import std/[assertions, syncio, formatfloat]
import ../../std/private/since import ../../std/private/since

View file

@ -165,7 +165,7 @@ import options # xxx remove this dependency using same approach as https://githu
import std/private/since import std/private/since
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/[syncio, assertions] import std/[syncio, assertions, formatfloat]
export export
tables.`$` tables.`$`

142
lib/std/formatfloat.nim Normal file
View file

@ -0,0 +1,142 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2022 Nim contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
when defined(nimPreviewSlimSystem):
import std/assertions
else:
{.deprecated: "formatfloat is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/formatfloat`".}
proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "<string.h>", discardable.}
proc addCstringN(result: var string, buf: cstring; buflen: int) =
# no nimvm support needed, so it doesn't need to be fast here either
let oldLen = result.len
let newLen = oldLen + buflen
result.setLen newLen
c_memcpy(result[oldLen].addr, buf, buflen.csize_t)
import std/private/[dragonbox, schubfach]
proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: BiggestFloat): int =
## This is the implementation to format floats.
##
## returns the amount of bytes written to `buf` not counting the
## terminating '\0' character.
result = toChars(buf, value, forceTrailingDotZero=true)
buf[result] = '\0'
proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int =
result = float32ToChars(buf, value, forceTrailingDotZero=true)
buf[result] = '\0'
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
importc: "sprintf", varargs, noSideEffect.}
proc writeToBuffer(buf: var array[65, char]; value: cstring) =
var i = 0
while value[i] != '\0':
buf[i] = value[i]
inc i
proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int =
## This is the implementation to format floats.
##
## returns the amount of bytes written to `buf` not counting the
## terminating '\0' character.
var n: int = c_sprintf(addr buf, "%.16g", value)
var hasDot = false
for i in 0..n-1:
if buf[i] == ',':
buf[i] = '.'
hasDot = true
elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
hasDot = true
if not hasDot:
buf[n] = '.'
buf[n+1] = '0'
buf[n+2] = '\0'
result = n + 2
else:
result = n
# On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)'
# of '-1.#IND' are produced.
# We want to get rid of these here:
if buf[n-1] in {'n', 'N', 'D', 'd', ')'}:
writeToBuffer(buf, "nan")
result = 3
elif buf[n-1] == 'F':
if buf[0] == '-':
writeToBuffer(buf, "-inf")
result = 4
else:
writeToBuffer(buf, "inf")
result = 3
proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} =
when defined(nimPreviewFloatRoundtrip) or defined(nimPreviewSlimSystem):
writeFloatToBufferRoundtrip(buf, value)
else:
writeFloatToBufferSprintf(buf, value)
proc addFloatRoundtrip*(result: var string; x: float | float32) =
when nimvm:
doAssert false
else:
var buffer {.noinit.}: array[65, char]
let n = writeFloatToBufferRoundtrip(buffer, x)
result.addCstringN(cstring(buffer[0].addr), n)
proc addFloatSprintf*(result: var string; x: float) =
when nimvm:
doAssert false
else:
var buffer {.noinit.}: array[65, char]
let n = writeFloatToBufferSprintf(buffer, x)
result.addCstringN(cstring(buffer[0].addr), n)
proc nimFloatToString(a: float): cstring =
## ensures the result doesn't print like an integer, i.e. return 2.0, not 2
# print `-0.0` properly
asm """
function nimOnlyDigitsOrMinus(n) {
return n.toString().match(/^-?\d+$/);
}
if (Number.isSafeInteger(`a`))
`result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0"
else {
`result` = `a`+""
if(nimOnlyDigitsOrMinus(`result`)){
`result` = `a`+".0"
}
}
"""
proc addFloat*(result: var string; x: float | float32) {.inline.} =
## Converts float to its string representation and appends it to `result`.
runnableExamples:
var
s = "foo:"
b = 45.67
s.addFloat(45.67)
assert s == "foo:45.67"
template impl =
when defined(nimPreviewFloatRoundtrip) or defined(nimPreviewSlimSystem):
addFloatRoundtrip(result, x)
else:
addFloatSprintf(result, x)
when defined(js):
when nimvm: impl()
else:
result.add nimFloatToString(x)
else: impl()
when defined(nimPreviewSlimSystem):
func `$`*(x: float | float32): string =
## Outplace version of `addFloat`.
result.addFloat(x)

View file

@ -30,7 +30,6 @@ when defined(nimPreviewSlimSystem):
const const
dtoaMinBufferLength*: cint = 64 dtoaMinBufferLength*: cint = 64
## --------------------------------------------------------------------------------------------------
## This file contains an implementation of Junekey Jeon's Dragonbox algorithm. ## This file contains an implementation of Junekey Jeon's Dragonbox algorithm.
## ##
## It is a simplified version of the reference implementation found here: ## It is a simplified version of the reference implementation found here:
@ -38,14 +37,13 @@ const
## ##
## The reference implementation also works with single-precision floating-point numbers and ## The reference implementation also works with single-precision floating-point numbers and
## has options to configure the rounding mode. ## has options to configure the rounding mode.
## --------------------------------------------------------------------------------------------------
template dragonbox_Assert*(x: untyped): untyped = template dragonbox_Assert*(x: untyped): untyped =
assert(x) assert(x)
## ================================================================================================== # ==================================================================================================
## #
## ================================================================================================== # ==================================================================================================
type type
ValueType* = float ValueType* = float
@ -106,10 +104,11 @@ proc isZero*(this: Double): bool {.noSideEffect.} =
proc signBit*(this: Double): int {.noSideEffect.} = proc signBit*(this: Double): int {.noSideEffect.} =
return ord((this.bits and signMask) != 0) return ord((this.bits and signMask) != 0)
# ==================================================================================================
#
# ==================================================================================================
## namespace ## namespace
## ==================================================================================================
##
## ==================================================================================================
## Returns floor(x / 2^n). ## Returns floor(x / 2^n).
## ##
## Technically, right-shift of negative integers is implementation defined... ## Technically, right-shift of negative integers is implementation defined...
@ -133,9 +132,9 @@ proc floorLog10ThreeQuartersPow2*(e: int32): int32 {.inline.} =
dragonbox_Assert(e <= 1500) dragonbox_Assert(e <= 1500)
return floorDivPow2(e * 1262611 - 524031, 22) return floorDivPow2(e * 1262611 - 524031, 22)
## ================================================================================================== # ==================================================================================================
## #
## ================================================================================================== # ==================================================================================================
type type
uint64x2* {.bycopy.} = object uint64x2* {.bycopy.} = object
@ -1040,9 +1039,9 @@ proc toDecimal64*(ieeeSignificand: uint64; ieeeExponent: uint64): FloatingDecima
dec(q) dec(q)
return FloatingDecimal64(significand: q, exponent: minusK + kappa) return FloatingDecimal64(significand: q, exponent: minusK + kappa)
## ================================================================================================== # ==================================================================================================
## ToChars # ToChars
## ================================================================================================== # ==================================================================================================
when false: when false:
template `+!`(x: cstring; offset: int): cstring = cast[cstring](cast[uint](x) + uint(offset)) template `+!`(x: cstring; offset: int): cstring = cast[cstring](cast[uint](x) + uint(offset))

View file

@ -3,12 +3,12 @@
## Distributed under the Boost Software License, Version 1.0. ## Distributed under the Boost Software License, Version 1.0.
## (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) ## (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
## -------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------
## This file contains an implementation of the Schubfach algorithm as described in ## This file contains an implementation of the Schubfach algorithm as described in
## ##
## [1] Raffaello Giulietti, "The Schubfach way to render doubles", ## [1] Raffaello Giulietti, "The Schubfach way to render doubles",
## https://drive.google.com/open?id=1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN ## https://drive.google.com/open?id=1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN
## -------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------
import std/private/digitsutils import std/private/digitsutils
@ -19,9 +19,9 @@ when defined(nimPreviewSlimSystem):
template sf_Assert(x: untyped): untyped = template sf_Assert(x: untyped): untyped =
assert(x) assert(x)
## ================================================================================================== # ==================================================================================================
## #
## ================================================================================================== # ==================================================================================================
type type
ValueType = float32 ValueType = float32
@ -68,7 +68,7 @@ proc isZero(this: Single): bool {.noSideEffect.} =
proc signBit(this: Single): int {.noSideEffect.} = proc signBit(this: Single): int {.noSideEffect.} =
return int((this.bits and signMask) != 0) return int((this.bits and signMask) != 0)
## ================================================================================================== # ==================================================================================================
## Returns floor(x / 2^n). ## Returns floor(x / 2^n).
## ##
## Technically, right-shift of negative integers is implementation defined... ## Technically, right-shift of negative integers is implementation defined...

View file

@ -11,7 +11,7 @@
include system/inclrtl include system/inclrtl
import std/private/since import std/private/since
import system/formatfloat import std/formatfloat
# ----------------- IO Part ------------------------------------------------ # ----------------- IO Part ------------------------------------------------
type type

View file

@ -9,6 +9,8 @@
# Implementation of some runtime checks. # Implementation of some runtime checks.
include system/indexerrors include system/indexerrors
when defined(nimPreviewSlimSystem):
import std/formatfloat
proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} = proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} =
when hostOS == "standalone": when hostOS == "standalone":

View file

@ -4,8 +4,14 @@ runnableExamples:
assert $(-2*3) == "-6" assert $(-2*3) == "-6"
import std/private/[digitsutils, miscdollars] import std/private/[digitsutils, miscdollars]
import system/formatfloat
export addFloat when not defined(nimPreviewSlimSystem):
import std/formatfloat
export addFloat
func `$`*(x: float | float32): string =
## Outplace version of `addFloat`.
result.addFloat(x)
proc `$`*(x: int): string {.raises: [].} = proc `$`*(x: int): string {.raises: [].} =
## Outplace version of `addInt`. ## Outplace version of `addInt`.
@ -27,9 +33,6 @@ gen(int)
gen(uint64) gen(uint64)
gen(int64) gen(int64)
func `$`*(x: float | float32): string =
## Outplace version of `addFloat`.
result.addFloat(x)
proc `$`*(x: bool): string {.magic: "BoolToStr", noSideEffect.} proc `$`*(x: bool): string {.magic: "BoolToStr", noSideEffect.}
## The stringify operator for a boolean argument. Returns `x` ## The stringify operator for a boolean argument. Returns `x`

View file

@ -1,135 +1,6 @@
# when not defined(nimPreviewSlimSystem):
# import std/formatfloat
# Nim's Runtime Library export formatfloat
# (c) Copyright 2019 Nim contributors {.deprecated: "use `std/formatfloat`".}
# else:
# See the file "copying.txt", included in this {.error: "use `std/formatfloat`".}
# distribution, for details about the copyright.
#
when defined(nimPreviewSlimSystem):
import std/assertions
proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "<string.h>", discardable.}
proc addCstringN(result: var string, buf: cstring; buflen: int) =
# no nimvm support needed, so it doesn't need to be fast here either
let oldLen = result.len
let newLen = oldLen + buflen
result.setLen newLen
c_memcpy(result[oldLen].addr, buf, buflen.csize_t)
import dragonbox, schubfach
proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: BiggestFloat): int =
## This is the implementation to format floats.
##
## returns the amount of bytes written to `buf` not counting the
## terminating '\0' character.
result = toChars(buf, value, forceTrailingDotZero=true)
buf[result] = '\0'
proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int =
result = float32ToChars(buf, value, forceTrailingDotZero=true)
buf[result] = '\0'
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
importc: "sprintf", varargs, noSideEffect.}
proc writeToBuffer(buf: var array[65, char]; value: cstring) =
var i = 0
while value[i] != '\0':
buf[i] = value[i]
inc i
proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int =
## This is the implementation to format floats.
##
## returns the amount of bytes written to `buf` not counting the
## terminating '\0' character.
var n: int = c_sprintf(addr buf, "%.16g", value)
var hasDot = false
for i in 0..n-1:
if buf[i] == ',':
buf[i] = '.'
hasDot = true
elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
hasDot = true
if not hasDot:
buf[n] = '.'
buf[n+1] = '0'
buf[n+2] = '\0'
result = n + 2
else:
result = n
# On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)'
# of '-1.#IND' are produced.
# We want to get rid of these here:
if buf[n-1] in {'n', 'N', 'D', 'd', ')'}:
writeToBuffer(buf, "nan")
result = 3
elif buf[n-1] == 'F':
if buf[0] == '-':
writeToBuffer(buf, "-inf")
result = 4
else:
writeToBuffer(buf, "inf")
result = 3
proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} =
when defined(nimPreviewFloatRoundtrip):
writeFloatToBufferRoundtrip(buf, value)
else:
writeFloatToBufferSprintf(buf, value)
proc addFloatRoundtrip*(result: var string; x: float | float32) =
when nimvm:
doAssert false
else:
var buffer {.noinit.}: array[65, char]
let n = writeFloatToBufferRoundtrip(buffer, x)
result.addCstringN(cstring(buffer[0].addr), n)
proc addFloatSprintf*(result: var string; x: float) =
when nimvm:
doAssert false
else:
var buffer {.noinit.}: array[65, char]
let n = writeFloatToBufferSprintf(buffer, x)
result.addCstringN(cstring(buffer[0].addr), n)
proc nimFloatToString(a: float): cstring =
## ensures the result doesn't print like an integer, i.e. return 2.0, not 2
# print `-0.0` properly
asm """
function nimOnlyDigitsOrMinus(n) {
return n.toString().match(/^-?\d+$/);
}
if (Number.isSafeInteger(`a`))
`result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0"
else {
`result` = `a`+""
if(nimOnlyDigitsOrMinus(`result`)){
`result` = `a`+".0"
}
}
"""
proc addFloat*(result: var string; x: float | float32) {.inline.} =
## Converts float to its string representation and appends it to `result`.
runnableExamples:
var
s = "foo:"
b = 45.67
s.addFloat(45.67)
assert s == "foo:45.67"
template impl =
when defined(nimPreviewFloatRoundtrip):
addFloatRoundtrip(result, x)
else:
addFloatSprintf(result, x)
when defined(js):
when nimvm: impl()
else:
result.add nimFloatToString(x)
else: impl()

View file

@ -1,5 +1,8 @@
include system/inclrtl include system/inclrtl
when defined(nimPreviewSlimSystem):
import std/formatfloat
proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}
## imported from typetraits ## imported from typetraits