flt_conv.inc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. {
  2. Copyright (C) 2013 by Max Nazhalov
  3. This file, in conjunction with FLT_CORE.INC, implements 2-way conversion
  4. among the binary floating-point value and its ASCII representation.
  5. This library is free software; you can redistribute it and/or modify it
  6. under the terms of the GNU Library General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or (at your
  8. option) any later version with the following modification:
  9. As a special exception, the copyright holders of this library give you
  10. permission to link this library with independent modules to produce an
  11. executable, regardless of the license terms of these independent modules,
  12. and to copy and distribute the resulting executable under terms of your
  13. choice, provided that you also meet, for each linked independent module,
  14. the terms and conditions of the license of that module. An independent
  15. module is a module which is not derived from or based on this library.
  16. If you modify this library, you may extend this exception to your version
  17. of the library, but you are not obligated to do so. If you do not wish to
  18. do so, delete this exception statement from your version.
  19. This program is distributed in the hope that it will be useful,
  20. but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  22. See the GNU Library General Public License for more details.
  23. You should have received a copy of the GNU Library General Public License
  24. along with this library; if not, write to the Free Software Foundation,
  25. Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  26. ****************************************************************************
  27. }
  28. {
  29. Algorithm for converting floating-point values to decimal representation
  30. implemented here has the formal name "Grisu" (basically, "Grisu1").
  31. It was developed by Florian Loitsch and was presented on the PLDI forum
  32. in the mid-2010. Detailed algorithm description and rationale can be found
  33. in his article, referenced below. [1]
  34. This implementation is purely based on [1], extrapolating the original
  35. algorithm on to floating point types other than the "double": namely
  36. "single", "extended" and "float128".
  37. For self-sufficiency, inverse conversion is also implemented.
  38. It uses strightforward approach, with the help of high-precision integer
  39. routines and tables used by Grisu. The main goal was to confirm the "internal
  40. identity", i.e. conversion path " float -> ASCII -> float " should recover
  41. the original floating-point representation so long as there are enough
  42. significant digits provided.
  43. Generally, the algorithm used has two major drawbacks:
  44. 1. Although "Grisu1" is seems precise in the sense of internal identity, as
  45. defined above, it is often produces suboptimal output, i.e., in
  46. exponential representation, output could end up with "...00001",
  47. "...99998" et al., despite of the fact that in those cases rounding-off
  48. the last 1 (or, less often, 2) digits would not break internal identity.
  49. Refer to [1] for further explanations.
  50. 2. Although "Grisu1" is REALLY fast on, e.g. i386, there can be significant
  51. performance impact on platforms which have long floating point, but
  52. rather "short" integer ALU (e.g., i8086 with i8087). Despite of the fact
  53. that an attempt was made to use only "uint32" when converting the
  54. "single" back and forth, all other floating-point types unavoidably
  55. require and heavily use "uint64" arithmetics.
  56. ---
  57. Implementation was intentionally split into 3 include files to simplify
  58. maintenance and to avoid complex multiple self-includings.
  59. This file is the root one. It, depending on the selected mode, defines
  60. proper conditionals, and then includes FLT_CORE.INC, which implements actual
  61. conversions. Basically, there are 2 possible compilation modes:
  62. 1. If condition "fpc_softfpu_implementation" is defined before including
  63. this file, it is assumed to be a part of SoftFPU. In this case, file
  64. FLT_CORE.INC is included once per every supported floating point type.
  65. Naming conflicts resolved with macros.
  66. Supported types are selected with the following pre-defined conditionals:
  67. SOFTFLOAT_ASCII_FLOAT32
  68. SOFTFLOAT_ASCII_FLOAT64
  69. SOFTFLOAT_ASCII_FLOATX80
  70. SOFTFLOAT_ASCII_FLOAT128
  71. This mode is basically intended to allow testing and debugging various
  72. conversion scenarios without having appropriate floating point hardware,
  73. using only sandbox provided by the SoftFPU. An implementation example of
  74. this mode is shown in the accompanying file SFPU_FORMAT.PP.
  75. 2. If condition "fpc_softfpu_implementation" is not defined, it is assumed
  76. to be a part of the system unit, or something else. In this case, file
  77. FLT_CORE.INC is included only once, and the actual floating point type
  78. must be defined as "ValReal" before including this file. Also, kind of
  79. the "ValReal" must be further specified with the following defines (in
  80. order of priority):
  81. SUPPORT_FLOAT128
  82. SUPPORT_EXTENDED
  83. SUPPORT_DOUBLE
  84. SUPPORT_SINGLE
  85. Overall behavior and definitions mimic the REAL2STR.INC file from the
  86. FPC source tree.
  87. BEWARE: there are two minor differences between the code generated for mode 1
  88. and mode 2, which can potentially lead to different results:
  89. <i> floating-point value packing and unpacking routines are implemented
  90. differently among these modes (see FLT_PACK.INC).
  91. <ii> also one internal subroutine involved into conversion to ASCII, namely
  92. "k_comp", has separate implementations for mode 1 and mode 2.
  93. Certainly, they should behave the same, and they have never been caught yet
  94. during testing, but who knows..
  95. ---
  96. References:
  97. [1] Florian Loitsch. Printing Floating-Point Numbers Quickly and Accurately
  98. with Integers. PLDI'10, June 5-10, 2010, Toronto, Ontario, Canada.
  99. http://florian.loitsch.com/publications/dtoa-pldi2010.pdf?attredirects=0
  100. [2] IEEE 754-2008, Standard for Floating-Point Arithmetic. IEEE, New York,
  101. Aug. 29 2008.
  102. ****************************************************************************
  103. }
  104. {$push}
  105. {$Q-,R-,B-}
  106. {$ifdef DEBUG}
  107. {$define grisu1_debug}
  108. {$C+}
  109. {$else}
  110. {$undef grisu1_debug}
  111. {$endif}
  112. (*-------------------------------------------------------
  113. | Compatibility settings
  114. *-------------------------------------------------------*)
  115. // FPC defaults to "real indefinite" QNaN value, which is negative.
  116. // Undefine to respect the sign provided during ASCII->float conversion.
  117. {$define GRISU1_A2F_QNAN_REAL_INDEFINITE}
  118. // Controls printing of NaN-sign.
  119. // Undefine to print NaN sign during float->ASCII conversion.
  120. {$define GRISU1_F2A_NAN_SIGNLESS} // IEEE does not interpret the sign of a NaN, so leave it defined.
  121. // Controls rounding of generated digits when formatting with narrowed
  122. // width (either fixed or exponential notation).
  123. // Traditionally, FPC and BP7/Delphi use "roundTiesToAway" mode.
  124. // Undefine to use "roundTiesToEven" approach.
  125. {$define GRISU1_F2A_HALF_ROUNDUP}
  126. // This one is a hack against Grusu sub-optimality.
  127. // It may be used only strictly together with GRISU1_F2A_HALF_ROUNDUP.
  128. // It does not violate most general rules due to the fact that it is
  129. // applicable only when formatting with narrowed width, where the fine
  130. // view is more desirable, and the precision is already lost, so it can
  131. // be used in general-purpose applications.
  132. // Refer to its implementation.
  133. {$define GRISU1_F2A_AGRESSIVE_ROUNDUP} // Defining this fixes several tests.
  134. // Controls rounding of over-required digits during ASCII->float.
  135. // Undefine to use "roundTiesToEven" approach. [Should have no much sense, though]
  136. {$undef GRISU1_A2F_HALF_ROUNDUP}
  137. // Controls which result is returned in case of error during ASCII->float.
  138. // FPC initializes result to "0.0".
  139. // Undefine to return (s)NAN.
  140. {$define GRISU1_A2F_ERROR_RET0} // Leave it defined, otherwise several irrelevant tests will break.
  141. // Undefine to enable SNaN support.
  142. // Note: IEEE [754-2008, page 31] requires (1) to recognize "SNaN" during
  143. // ASCII->float, and (2) to generate the "invalid FP operation" exception
  144. // either when SNaN is printed as "NaN", or "SNaN" is evaluated to QNaN,
  145. // so it would be preferable to undefine these settings,
  146. // but the FPC RTL is not ready for this right now..
  147. {$define GRISU1_F2A_NO_SNAN}
  148. {$define GRISU1_A2F_NO_SNAN}
  149. // Controls how many digits is printed for "single".
  150. // IEEE claims 9 digits is always enough, and it is confirmed by
  151. // comprehensive testing.
  152. // Define to print 10 digits.
  153. // Note: there is no much sense to print 10 digits, except that it fixes
  154. // the test "./tests/test/units/sysutils/tfloattostr.pp".
  155. // Alternate way would be to print 9 digits, and change the test to
  156. // compare against "6e-9" instead of "6e-10".
  157. {$define GRISU1_F2A_SINGLE_10DIGITS}
  158. (*-------------------------------------------------------
  159. | These conditional defines are heavily used internally,
  160. | so make sure they are not defined around
  161. *-------------------------------------------------------*)
  162. {$ifdef VALREAL_32}
  163. {$fatal VALREAL_32 should not be defined here!}
  164. {$endif}
  165. {$ifdef VALREAL_64}
  166. {$fatal VALREAL_64 should not be defined here!}
  167. {$endif}
  168. {$ifdef VALREAL_80}
  169. {$fatal VALREAL_80 should not be defined here!}
  170. {$endif}
  171. {$ifdef VALREAL_128}
  172. {$fatal VALREAL_128 should not be defined here!}
  173. {$endif}
  174. {$ifdef VALREAL_PACK}
  175. {$fatal VALREAL_PACK should not be defined here!}
  176. {$endif}
  177. (*-------------------------------------------------------
  178. | Floating point types formatting profile
  179. *-------------------------------------------------------*)
  180. type
  181. TReal_Type = (
  182. RT_S32REAL, // single
  183. RT_S64REAL, // double
  184. RT_S80REAL, // extended [80-bit]
  185. RT_SC80REAL, // extended ["C-extended"; functionally the same as RT_S80REAL, but may be different in alignment and padding]
  186. RT_C64BIT, // comp [legacy; just an int64 passed via float]
  187. RT_CURRENCY, // currency [seems never passed to str_real since it has its own dedicated converters after r5866]
  188. RT_S128REAL // float128
  189. );
  190. // JVM target: explicitly define this record type to avoid "ie2011032601"
  191. TFloatFormatProfile = record
  192. nDig_mantissa, nDig_exp10: integer;
  193. end;
  194. const
  195. float_format: array [ TReal_Type ] of TFloatFormatProfile = (
  196. {
  197. Number of mantissa digits is dictated by [2] "IEEE 754-2008", page 32.
  198. N = 1 + ceiling( p * log10(2) ), where p is the number of significant
  199. bits, i.e. 24/53/64/113 accordingly (including the implicit one if any)
  200. }
  201. // RT_S32REAL
  202. ( nDig_mantissa: {$ifdef GRISU1_F2A_SINGLE_10DIGITS} 10 {$else} 9 {$endif};
  203. nDig_exp10: 2;
  204. ),
  205. // RT_S64REAL
  206. ( nDig_mantissa: 17;
  207. nDig_exp10: 3;
  208. ),
  209. // RT_S80REAL
  210. ( nDig_mantissa: 21;
  211. nDig_exp10: 4;
  212. ),
  213. // RT_SC80REAL
  214. ( nDig_mantissa: 21;
  215. nDig_exp10: 4;
  216. ),
  217. // RT_C64BIT
  218. ( nDig_mantissa: 19; // int64 has 19 digits
  219. nDig_exp10: 4; // Delphi prints exponent with 4 digits
  220. ),
  221. // RT_CURRENCY [seems not used after r5866]
  222. ( nDig_mantissa: 19;
  223. nDig_exp10: 2;
  224. ),
  225. // RT_S128REAL
  226. ( nDig_mantissa: 36;
  227. nDig_exp10: 4;
  228. )
  229. );
  230. C_STR_INF : string[3] = 'Inf';
  231. C_STR_QNAN : string[3] = 'Nan';
  232. {$if not ( defined(GRISU1_F2A_NO_SNAN) and defined(GRISU1_A2F_NO_SNAN) )}
  233. C_STR_SNAN : string[4] = 'SNan';
  234. {$endif GRISU1_*_NO_SNAN}
  235. {$ifdef fpc_softfpu_implementation}
  236. (****************************************************************************
  237. *
  238. * SoftPFU unit: Multiple instances for all supported floating point types
  239. *
  240. ****************************************************************************)
  241. {$inline on}
  242. {$macro on}
  243. {$define grisu1_inline}
  244. {$define ValSInt:=integer}
  245. {$ifdef SOFTFLOAT_ASCII_FLOAT32}
  246. {$define VALREAL_32}
  247. {$undef VALREAL_64}
  248. {$undef VALREAL_80}
  249. {$undef VALREAL_128}
  250. // Name remapping
  251. {$define val_real:=ascii_to_float32}
  252. {$define ValReal:=float32rec}
  253. {$define TDIY_FP:=TDIY_FP32}
  254. {$define TDIY_FP_Power_of_10:=TDIY_FP32_Power_of_10}
  255. // Implementation
  256. {$info === float32<->ASCII ===}
  257. {$i flt_core.inc}
  258. {$endif SOFTFLOAT_ASCII_FLOAT32}
  259. {$ifdef SOFTFLOAT_ASCII_FLOAT64}
  260. {$undef VALREAL_32}
  261. {$define VALREAL_64}
  262. {$undef VALREAL_80}
  263. {$undef VALREAL_128}
  264. // Name remapping
  265. {$define val_real:=ascii_to_float64}
  266. {$define ValReal:=float64}
  267. {$define TDIY_FP:=TDIY_FP64}
  268. {$define TDIY_FP_Power_of_10:=TDIY_FP64_Power_of_10}
  269. // Implementation
  270. {$info === float64<->ASCII ===}
  271. {$i flt_core.inc}
  272. {$endif SOFTFLOAT_ASCII_FLOAT64}
  273. {$ifdef SOFTFLOAT_ASCII_FLOATX80}
  274. {$undef VALREAL_32}
  275. {$undef VALREAL_64}
  276. {$define VALREAL_80}
  277. {$undef VALREAL_128}
  278. // Name remapping
  279. {$define val_real:=ascii_to_floatx80}
  280. {$define ValReal:=floatx80}
  281. {$define TDIY_FP:=TDIY_FP96}
  282. {$define TDIY_FP_Power_of_10:=TDIY_FP96_Power_of_10}
  283. // Implementation
  284. {$info === floatx80<->ASCII ===}
  285. {$i flt_core.inc}
  286. {$endif SOFTFLOAT_ASCII_FLOATX80}
  287. {$ifdef SOFTFLOAT_ASCII_FLOAT128}
  288. {$undef VALREAL_32}
  289. {$undef VALREAL_64}
  290. {$undef VALREAL_80}
  291. {$define VALREAL_128}
  292. // Name remapping
  293. {$define val_real:=ascii_to_float128}
  294. {$define ValReal:=float128}
  295. {$define TDIY_FP:=TDIY_FP128}
  296. {$define TDIY_FP_Power_of_10:=TDIY_FP128_Power_of_10}
  297. // Implementation
  298. {$info === float128<->ASCII ===}
  299. {$i flt_core.inc}
  300. {$endif SOFTFLOAT_ASCII_FLOAT128}
  301. // Clean-up
  302. {$undef VALREAL_32}
  303. {$undef VALREAL_64}
  304. {$undef VALREAL_80}
  305. {$undef VALREAL_128}
  306. {$undef VALREAL_PACK}
  307. {$undef val_real}
  308. {$undef ValReal}
  309. {$undef ValSInt}
  310. {$undef TDIY_FP}
  311. {$undef TDIY_FP_Power_of_10}
  312. {$undef grisu1_inline}
  313. {$info ========================}
  314. {$else not fpc_softfpu_implementation}
  315. (****************************************************************************
  316. *
  317. * System unit: only one native floating point type
  318. *
  319. ****************************************************************************)
  320. {$ifdef SYSTEMINLINE}
  321. {$define grisu1_inline}
  322. {$else}
  323. {$undef grisu1_inline}
  324. {$endif}
  325. {$if defined(SUPPORT_FLOAT128)}
  326. {$undef VALREAL_32}
  327. {$undef VALREAL_64}
  328. {$undef VALREAL_80}
  329. {$define VALREAL_128}
  330. {$elseif defined(SUPPORT_EXTENDED)}
  331. {$undef VALREAL_32}
  332. {$undef VALREAL_64}
  333. {$define VALREAL_80}
  334. {$undef VALREAL_128}
  335. {$elseif defined(SUPPORT_DOUBLE)}
  336. {$undef VALREAL_32}
  337. {$define VALREAL_64}
  338. {$undef VALREAL_80}
  339. {$undef VALREAL_128}
  340. {$elseif defined(SUPPORT_SINGLE)}
  341. {$define VALREAL_32}
  342. {$undef VALREAL_64}
  343. {$undef VALREAL_80}
  344. {$undef VALREAL_128}
  345. {$else}
  346. {$error Unrecognized ValReal type}
  347. {$endif SUPPORT_*}
  348. // Implementation
  349. {$i flt_core.inc}
  350. // Clean-up
  351. { undef VALREAL_32}
  352. { undef VALREAL_64}
  353. { undef VALREAL_80}
  354. { undef VALREAL_128}
  355. {$undef VALREAL_PACK}
  356. {$undef grisu1_inline}
  357. {$endif fpc_softfpu_implementation}
  358. {$pop}