formatfloat.nim 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2019 Nim contributors
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "<string.h>", discardable.}
  10. proc addCstringN(result: var string, buf: cstring; buflen: int) =
  11. # no nimvm support needed, so it doesn't need to be fast here either
  12. let oldLen = result.len
  13. let newLen = oldLen + buflen
  14. result.setLen newLen
  15. c_memcpy(result[oldLen].addr, buf, buflen.csize_t)
  16. import dragonbox, schubfach
  17. proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: BiggestFloat): int =
  18. ## This is the implementation to format floats.
  19. ##
  20. ## returns the amount of bytes written to `buf` not counting the
  21. ## terminating '\0' character.
  22. result = toChars(buf, value, forceTrailingDotZero=true)
  23. buf[result] = '\0'
  24. proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int =
  25. result = float32ToChars(buf, value, forceTrailingDotZero=true)
  26. buf[result] = '\0'
  27. proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
  28. importc: "sprintf", varargs, noSideEffect.}
  29. proc writeToBuffer(buf: var array[65, char]; value: cstring) =
  30. var i = 0
  31. while value[i] != '\0':
  32. buf[i] = value[i]
  33. inc i
  34. proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int =
  35. ## This is the implementation to format floats.
  36. ##
  37. ## returns the amount of bytes written to `buf` not counting the
  38. ## terminating '\0' character.
  39. var n: int = c_sprintf(addr buf, "%.16g", value)
  40. var hasDot = false
  41. for i in 0..n-1:
  42. if buf[i] == ',':
  43. buf[i] = '.'
  44. hasDot = true
  45. elif buf[i] in {'a'..'z', 'A'..'Z', '.'}:
  46. hasDot = true
  47. if not hasDot:
  48. buf[n] = '.'
  49. buf[n+1] = '0'
  50. buf[n+2] = '\0'
  51. result = n + 2
  52. else:
  53. result = n
  54. # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)'
  55. # of '-1.#IND' are produced.
  56. # We want to get rid of these here:
  57. if buf[n-1] in {'n', 'N', 'D', 'd', ')'}:
  58. writeToBuffer(buf, "nan")
  59. result = 3
  60. elif buf[n-1] == 'F':
  61. if buf[0] == '-':
  62. writeToBuffer(buf, "-inf")
  63. result = 4
  64. else:
  65. writeToBuffer(buf, "inf")
  66. result = 3
  67. proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} =
  68. when defined(nimPreviewFloatRoundtrip):
  69. writeFloatToBufferRoundtrip(buf, value)
  70. else:
  71. writeFloatToBufferSprintf(buf, value)
  72. proc addFloatRoundtrip*(result: var string; x: float | float32) =
  73. when nimvm:
  74. doAssert false
  75. else:
  76. var buffer {.noinit.}: array[65, char]
  77. let n = writeFloatToBufferRoundtrip(buffer, x)
  78. result.addCstringN(cstring(buffer[0].addr), n)
  79. proc addFloatSprintf*(result: var string; x: float) =
  80. when nimvm:
  81. doAssert false
  82. else:
  83. var buffer {.noinit.}: array[65, char]
  84. let n = writeFloatToBufferSprintf(buffer, x)
  85. result.addCstringN(cstring(buffer[0].addr), n)
  86. proc nimFloatToString(a: float): cstring =
  87. ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2
  88. # print `-0.0` properly
  89. asm """
  90. function nimOnlyDigitsOrMinus(n) {
  91. return n.toString().match(/^-?\d+$/);
  92. }
  93. if (Number.isSafeInteger(`a`))
  94. `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0"
  95. else {
  96. `result` = `a`+""
  97. if(nimOnlyDigitsOrMinus(`result`)){
  98. `result` = `a`+".0"
  99. }
  100. }
  101. """
  102. proc addFloat*(result: var string; x: float | float32) {.inline.} =
  103. ## Converts float to its string representation and appends it to `result`.
  104. runnableExamples:
  105. var
  106. s = "foo:"
  107. b = 45.67
  108. s.addFloat(45.67)
  109. assert s == "foo:45.67"
  110. template impl =
  111. when defined(nimPreviewFloatRoundtrip):
  112. addFloatRoundtrip(result, x)
  113. else:
  114. addFloatSprintf(result, x)
  115. when defined(js):
  116. when nimvm: impl()
  117. else:
  118. result.add nimFloatToString(x)
  119. else: impl()