VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/string/strtofloat.cpp@ 96152

Last change on this file since 96152 was 96152, checked in by vboxsync, 3 years ago

IPRT: Added RTStrToFloat, RTStrToDouble and RTStrToLongDouble. Still some rought edges; only tested on Windows. bugref:10261

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.6 KB
Line 
1/* $Id: strtofloat.cpp 96152 2022-08-11 23:49:45Z vboxsync $ */
2/** @file
3 * IPRT - String To Floating Point Conversion.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/string.h>
32#include "internal/iprt.h"
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/ctype.h> /* needed for RT_C_IS_DIGIT */
37#include <iprt/err.h>
38
39#include <float.h>
40#include <math.h>
41#if !defined(_MSC_VER) || !defined(IPRT_NO_CRT) /** @todo fix*/
42# include <fenv.h>
43#endif
44
45#if defined(SOFTFLOAT_FAST_INT64) /** @todo better softfloat indicator? */
46# define USE_SOFTFLOAT /* for scaling by power of 10 */
47#endif
48#ifdef USE_SOFTFLOAT
49# include <softfloat.h>
50#endif
51
52
53/*********************************************************************************************************************************
54* Structures and Typedefs *
55*********************************************************************************************************************************/
56typedef struct FLOATUNION
57{
58 RTFLOAT80U2 lrd;
59 RTFLOAT64U rd;
60 RTFLOAT32U r;
61} FLOATUNION;
62
63#define RET_TYPE_FLOAT 0
64#define RET_TYPE_DOUBLE 1
65#define RET_TYPE_LONG_DOUBLE 2
66
67#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
68typedef RTFLOAT80U2 LONG_DOUBLE_U_T;
69#else
70typedef RTFLOAT64U LONG_DOUBLE_U_T;
71#endif
72
73
74/*********************************************************************************************************************************
75* Global Variables *
76*********************************************************************************************************************************/
77/* in strtonum.cpp */
78extern const unsigned char g_auchDigits[256];
79
80#define DIGITS_ZERO_TERM 254
81#define DIGITS_COLON 253
82#define DIGITS_SPACE 252
83#define DIGITS_DOT 251
84
85#if 0
86/** Maximum exponent value in the binary representation for a RET_TYPE_XXX. */
87static const int32_t g_iMaxExp[3] =
88{
89 RTFLOAT32U_EXP_MAX - 1 - RTFLOAT32U_EXP_BIAS,
90 RTFLOAT64U_EXP_MAX - 1 - RTFLOAT64U_EXP_BIAS,
91#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
92 RTFLOAT80U_EXP_MAX - 1 - RTFLOAT80U_EXP_BIAS,
93#else
94 RTFLOAT64U_EXP_MAX - 1 - RTFLOAT64U_EXP_BIAS,
95#endif
96};
97
98/** Minimum exponent value in the binary representation for a RET_TYPE_XXX. */
99static const int32_t g_iMinExp[3] =
100{
101 1 - RTFLOAT32U_EXP_BIAS,
102 1 - RTFLOAT64U_EXP_BIAS,
103#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
104 1 - RTFLOAT80U_EXP_BIAS,
105#else
106 1 - RTFLOAT64U_EXP_BIAS,
107#endif
108};
109#endif
110
111/** NaN fraction value masks. */
112static uint64_t const g_fNanMasks[3] =
113{
114 RT_BIT_64(RTFLOAT32U_FRACTION_BITS - 1) - 1, /* 22=quiet(1) / silent(0) */
115 RT_BIT_64(RTFLOAT64U_FRACTION_BITS - 1) - 1, /* 51=quiet(1) / silent(0) */
116#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
117 RT_BIT_64(RTFLOAT80U_FRACTION_BITS - 1) - 1, /* bit 63=NaN; bit 62=quiet(1) / silent(0) */
118#else
119 RT_BIT_64(RTFLOAT64U_FRACTION_BITS - 1) - 1,
120#endif
121};
122
123#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
124static const long double g_lrdPowerMin10 = 1e4931L;
125static const long double g_lrdPowerMax10 = 1e4932L;
126#else
127static const long double g_lrdPowerMin10 = 1e307L;
128static const long double g_lrdPowerMax10 = 1e308L;
129#endif
130
131#ifdef USE_SOFTFLOAT
132/** SoftFloat: Power of 10 table using 128-bit floating point.
133 *
134 * @code
135 softfloat_state_t SoftState = SOFTFLOAT_STATE_INIT_DEFAULTS();
136 float128_t Power10;
137 ui32_to_f128M(10, &Power10, &SoftState);
138 for (unsigned iBit = 0; iBit < 13; iBit++)
139 {
140 RTAssertMsg2(" { %#018RX64, %#018RX64 }, %c* 1e%u (%RU64) *%c\n", Power10.v[0], Power10.v[1],
141 '/', RT_BIT_32(iBit), f128M_to_ui64(&Power10, softfloat_round_near_even, false, &SoftState), '/');
142 f128M_mul(&Power10, &Power10, &Power10, &SoftState);
143 }
144 @endcode */
145static const float128_t g_ar128Power10[] =
146{
147 { 0x0000000000000000, 0x4002400000000000 }, /* 1e1 (10) */
148 { 0x0000000000000000, 0x4005900000000000 }, /* 1e2 (100) */
149 { 0x0000000000000000, 0x400c388000000000 }, /* 1e4 (10000) */
150 { 0x0000000000000000, 0x40197d7840000000 }, /* 1e8 (100000000) */
151 { 0x0000000000000000, 0x40341c37937e0800 }, /* 1e16 (10000000000000000) */
152 { 0x6b3be04000000000, 0x40693b8b5b5056e1 }, /* 1e32 (18446744073709551615) */
153 { 0x4daa797ed6e38ed6, 0x40d384f03e93ff9f }, /* 1e64 (18446744073709551615) */
154 { 0x19bf8cde66d86d61, 0x41a827748f9301d3 }, /* 1e128 (18446744073709551615) */
155 { 0xbd1bbb77203731fb, 0x435154fdd7f73bf3 }, /* 1e256 (18446744073709551615) */
156 { 0x238d98cab8a97899, 0x46a3c633415d4c1d }, /* 1e512 (18446744073709551615) */
157 { 0x182eca1a7a51e308, 0x4d4892eceb0d02ea }, /* 1e1024 (18446744073709551615) */
158 { 0xbbc94e9a519c651e, 0x5a923d1676bb8a7a }, /* 1e2048 (18446744073709551615) */
159 { 0x2f3592982a7f005a, 0x752588c0a4051441 }, /* 1e4096 (18446744073709551615) */
160 /* INF */
161};
162
163/** SoftFloat: Initial value for power of 10 scaling.
164 * This deals with the first 32 powers of 10, covering the a full 64-bit
165 * mantissa and a small exponent w/o needing to make use of g_ar128Power10.
166 *
167 * @code
168 softfloat_state_t SoftState = SOFTFLOAT_STATE_INIT_DEFAULTS();
169 float128_t Num10;
170 ui32_to_f128M(10, &Num10, &SoftState);
171 float128_t Power10;
172 ui32_to_f128M(1, &Power10, &SoftState);
173 for (unsigned cTimes = 0; cTimes < 32; cTimes++)
174 {
175 RTAssertMsg2(" { %#018RX64, %#018RX64 }, %c* 1e%u (%RU64) *%c\n", Power10.v[0], Power10.v[1],
176 '/', cTimes, f128M_to_ui64(&Power10, softfloat_round_near_even, false, &SoftState), '/');
177 f128M_mul(&Power10, &Num10, &Power10, &SoftState);
178 }
179 @endcode */
180static const float128_t g_ar128Power10Initial[] =
181{
182 { 0x0000000000000000, 0x3fff000000000000 }, /* 1e0 (1) */
183 { 0x0000000000000000, 0x4002400000000000 }, /* 1e1 (10) */
184 { 0x0000000000000000, 0x4005900000000000 }, /* 1e2 (100) */
185 { 0x0000000000000000, 0x4008f40000000000 }, /* 1e3 (1000) */
186 { 0x0000000000000000, 0x400c388000000000 }, /* 1e4 (10000) */
187 { 0x0000000000000000, 0x400f86a000000000 }, /* 1e5 (100000) */
188 { 0x0000000000000000, 0x4012e84800000000 }, /* 1e6 (1000000) */
189 { 0x0000000000000000, 0x4016312d00000000 }, /* 1e7 (10000000) */
190 { 0x0000000000000000, 0x40197d7840000000 }, /* 1e8 (100000000) */
191 { 0x0000000000000000, 0x401cdcd650000000 }, /* 1e9 (1000000000) */
192 { 0x0000000000000000, 0x40202a05f2000000 }, /* 1e10 (10000000000) */
193 { 0x0000000000000000, 0x402374876e800000 }, /* 1e11 (100000000000) */
194 { 0x0000000000000000, 0x4026d1a94a200000 }, /* 1e12 (1000000000000) */
195 { 0x0000000000000000, 0x402a2309ce540000 }, /* 1e13 (10000000000000) */
196 { 0x0000000000000000, 0x402d6bcc41e90000 }, /* 1e14 (100000000000000) */
197 { 0x0000000000000000, 0x4030c6bf52634000 }, /* 1e15 (1000000000000000) */
198 { 0x0000000000000000, 0x40341c37937e0800 }, /* 1e16 (10000000000000000) */
199 { 0x0000000000000000, 0x40376345785d8a00 }, /* 1e17 (100000000000000000) */
200 { 0x0000000000000000, 0x403abc16d674ec80 }, /* 1e18 (1000000000000000000) */
201 { 0x0000000000000000, 0x403e158e460913d0 }, /* 1e19 (10000000000000000000) */
202 { 0x0000000000000000, 0x40415af1d78b58c4 }, /* 1e20 (18446744073709551615) */
203 { 0x0000000000000000, 0x4044b1ae4d6e2ef5 }, /* 1e21 (18446744073709551615) */
204 { 0x2000000000000000, 0x40480f0cf064dd59 }, /* 1e22 (18446744073709551615) */
205 { 0x6800000000000000, 0x404b52d02c7e14af }, /* 1e23 (18446744073709551615) */
206 { 0x4200000000000000, 0x404ea784379d99db }, /* 1e24 (18446744073709551615) */
207 { 0x0940000000000000, 0x405208b2a2c28029 }, /* 1e25 (18446744073709551615) */
208 { 0x4b90000000000000, 0x40554adf4b732033 }, /* 1e26 (18446744073709551615) */
209 { 0x1e74000000000000, 0x40589d971e4fe840 }, /* 1e27 (18446744073709551615) */
210 { 0x1308800000000000, 0x405c027e72f1f128 }, /* 1e28 (18446744073709551615) */
211 { 0x17caa00000000000, 0x405f431e0fae6d72 }, /* 1e29 (18446744073709551615) */
212 { 0x9dbd480000000000, 0x406293e5939a08ce }, /* 1e30 (18446744073709551615) */
213 { 0x452c9a0000000000, 0x4065f8def8808b02 }, /* 1e31 (18446744073709551615) */
214};
215
216#else /* !USE_SOFTFLOAT */
217/** Long Double: Power of 10 table scaling table.
218 * @note LDBL_MAX_10_EXP is 4932 for 80-bit and 308 for 64-bit type. */
219static const long double a_lrdPower10[] =
220{
221 1e1L,
222 1e2L,
223 1e4L,
224 1e8L,
225 1e16L,
226 1e32L,
227 1e64L,
228 1e128L,
229 1e256L,
230# ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
231 1e512L,
232 1e1024L,
233 1e2048L,
234 1e4096L,
235# endif
236};
237
238/** Long double: Initial value for power of 10 scaling.
239 * This deals with the first 32 powers of 10, covering the a full 64-bit
240 * mantissa and a small exponent w/o needing to make use of g_ar128Power10. */
241static const long double g_alrdPower10Initial[] =
242{
243 1e0L,
244 1e1L,
245 1e2L,
246 1e3L,
247 1e4L,
248 1e5L,
249 1e6L,
250 1e7L,
251 1e8L,
252 1e9L,
253 1e10L,
254 1e11L,
255 1e12L,
256 1e13L,
257 1e14L,
258 1e15L,
259 1e16L,
260 1e17L,
261 1e18L,
262 1e19L,
263 1e20L,
264 1e21L,
265 1e22L,
266 1e23L,
267 1e24L,
268 1e25L,
269 1e26L,
270 1e27L,
271 1e28L,
272 1e29L,
273 1e30L,
274 1e31L,
275};
276
277/* Tell the compiler that we'll mess with the FPU environment. */
278# ifdef _MSC_VER
279# pragma fenv_access(on)
280# endif
281#endif /*!USE_SOFTFLOAT */
282
283
284/**
285 * Multiply @a pVal by 10 to the power of @a iExponent10.
286 *
287 * This is currently a weak point where we might end up with rounding issues.
288 */
289static int rtStrToLongDoubleExp10(LONG_DOUBLE_U_T *pVal, int iExponent10)
290{
291 AssertReturn(iExponent10 != 0, VINF_SUCCESS);
292#ifdef USE_SOFTFLOAT
293 /* Use 128-bit precision floating point from softfloat to improve accuracy. */
294
295 softfloat_state_t SoftState = SOFTFLOAT_STATE_INIT_DEFAULTS();
296 float128_t Val;
297#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
298 extFloat80M Tmp = EXTFLOAT80M_INIT(pVal->s2.uSignAndExponent, pVal->s2.uMantissa);
299 extF80M_to_f128M(&Tmp, &Val, &SoftState);
300#else
301 float64_t Tmp = { pVal->u };
302 f64_to_f128M(Tmp, &Val, &SoftState);
303#endif
304
305 /*
306 * Calculate the scaling factor. If we need to make use of the last table
307 * entry, we will do part of the scaling here to avoid overflowing Factor.
308 */
309 unsigned uAbsExp = (unsigned)RT_ABS(iExponent10);
310 AssertCompile(RT_ELEMENTS(g_ar128Power10Initial) == 32);
311 unsigned iBit = 5;
312 float128_t Factor = g_ar128Power10Initial[uAbsExp & 31];
313 uAbsExp >>= iBit;
314 while (uAbsExp != 0)
315 {
316 if (iBit < RT_ELEMENTS(g_ar128Power10))
317 {
318 if (uAbsExp & 1)
319 {
320 if (iBit < RT_ELEMENTS(g_ar128Power10) - 1)
321 f128M_mul(&Factor, &g_ar128Power10[iBit], &Factor, &SoftState);
322 else
323 {
324 /* Must do it in two steps to avoid prematurely overflowing the factor value. */
325 if (iExponent10 > 0)
326 f128M_mul(&Val, &Factor, &Val, &SoftState);
327 else
328 f128M_div(&Val, &Factor, &Val, &SoftState);
329 Factor = g_ar128Power10[iBit];
330 }
331 }
332 }
333 else if (iExponent10 < 0)
334 {
335 pVal->r = pVal->r < 0.0L ? -0.0L : +0.0L;
336 return VERR_FLOAT_UNDERFLOW;
337 }
338 else
339 {
340 pVal->r = pVal->r < 0.0L ? -INFINITY : +INFINITY;
341 return VERR_FLOAT_OVERFLOW;
342 }
343 iBit++;
344 uAbsExp >>= 1;
345 }
346
347 /*
348 * Do the scaling (or what remains).
349 */
350 if (iExponent10 > 0)
351 f128M_mul(&Val, &Factor, &Val, &SoftState);
352 else
353 f128M_div(&Val, &Factor, &Val, &SoftState);
354
355#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
356 f128M_to_extF80M(&Val, &Tmp, &SoftState);
357 pVal->s2.uSignAndExponent = Tmp.signExp;
358 pVal->s2.uMantissa = Tmp.signif;
359#else
360 Tmp = f128M_to_f64(&Val, &SoftState);
361 pVal->u = Tmp.v;
362#endif
363
364 /*
365 * Check for under/overflow and return.
366 */
367 int rc;
368 if (!(SoftState.exceptionFlags & (softfloat_flag_underflow | softfloat_flag_overflow)))
369 rc = VINF_SUCCESS;
370 else if (SoftState.exceptionFlags & softfloat_flag_underflow)
371 {
372RTAssertMsg2("VERR_FLOAT_UNDERFLOW r128=%.16Rhxs r64=%.8Rhxs\n", &Val, &Tmp);
373 rc = VERR_FLOAT_UNDERFLOW;
374 }
375 else
376 rc = VERR_FLOAT_OVERFLOW;
377
378#else
379# if 0
380 /*
381 * Use RTBigNum, falling back on the simple approach if we don't need the
382 * precision or run out of memory?
383 */
384 /** @todo implement RTBigNum approach */
385# endif
386
387 /*
388 * Simple approach.
389 */
390# if !defined(_MSC_VER) || !defined(IPRT_NO_CRT) /** @todo fix*/
391 fenv_t SavedFpuEnv;
392 feholdexcept(&SavedFpuEnv);
393# endif
394
395 /*
396 * Calculate the scaling factor. If we need to make use of the last table
397 * entry, we will do part of the scaling here to avoid overflowing lrdFactor.
398 */
399 AssertCompile(RT_ELEMENTS(g_alrdPower10Initial) == 32);
400 int rc = VINF_SUCCESS;
401 unsigned uAbsExp = (unsigned)RT_ABS(iExponent10);
402 long double lrdFactor = g_alrdPower10Initial[uAbsExp & 31];
403 unsigned iBit = 5;
404 uAbsExp >>= iBit;
405
406 while (uAbsExp != 0)
407 {
408 if (iBit < RT_ELEMENTS(a_lrdPower10))
409 {
410 if (uAbsExp & 1)
411 {
412 if (iBit < RT_ELEMENTS(a_lrdPower10) - 1)
413 lrdFactor *= a_lrdPower10[iBit];
414 else
415 {
416 /* Must do it in two steps to avoid prematurely overflowing the factor value. */
417 if (iExponent10 < 0)
418 pVal->r /= lrdFactor;
419 else
420 pVal->r *= lrdFactor;
421 lrdFactor = a_lrdPower10[iBit];
422 }
423 }
424 }
425 else if (iExponent10 < 0)
426 {
427 pVal->r = pVal->r < 0.0L ? -0.0L : +0.0L;
428 rc = VERR_FLOAT_UNDERFLOW;
429 break;
430 }
431 else
432 {
433 pVal->r = pVal->r < 0.0L ? -INFINITY : +INFINITY;
434 rc = VERR_FLOAT_OVERFLOW;
435 break;
436 }
437 iBit++;
438 uAbsExp >>= 1;
439 }
440
441 /*
442 * Do the scaling (or what remains).
443 */
444 if (iExponent10 < 0)
445 pVal->r /= lrdFactor;
446 else
447 pVal->r *= lrdFactor;
448
449# if !defined(_MSC_VER) || !defined(IPRT_NO_CRT) /** @todo fix*/
450 fesetenv(&SavedFpuEnv);
451# endif
452
453#endif
454 return rc;
455}
456
457
458
459/**
460 * Set @a ppszNext and check for trailing spaces & chars if @a rc is
461 * VINF_SUCCESS.
462 *
463 * @returns IPRT status code.
464 * @param psz The current input position.
465 * @param ppszNext Where to return the pointer to the end of the value.
466 * Optional.
467 * @param cchMax Number of bytes left in the string starting at @a psz.
468 * @param rc The status code to return.
469 */
470static int rtStrToLongDoubleReturnChecks(const char *psz, char **ppszNext, size_t cchMax, int rc)
471{
472 if (ppszNext)
473 *ppszNext = (char *)psz;
474
475 /* Trailing spaces/chars warning: */
476 if (rc == VINF_SUCCESS && cchMax > 0 && *psz)
477 {
478 do
479 {
480 char ch = *psz++;
481 if (ch == ' ' || ch == '\t')
482 cchMax--;
483 else
484 return ch == '\0' ? VWRN_TRAILING_SPACES : VWRN_TRAILING_CHARS;
485 } while (cchMax > 0);
486 rc = VWRN_TRAILING_SPACES;
487 }
488 return rc;
489}
490
491
492/**
493 * Set @a pRet to infinity, set @a ppszNext, and check for trailing spaces &
494 * chars if @a rc is VINF_SUCCESS.
495 *
496 * @returns IPRT status code.
497 * @param psz The current input position.
498 * @param ppszNext Where to return the pointer to the end of the value.
499 * Optional.
500 * @param cchMax Number of bytes left in the string starting at @a psz.
501 * @param rc The status code to return.
502 */
503static int rtStrToLongDoubleReturnInf(const char *psz, char **ppszNext, size_t cchMax, bool fPositive,
504 int rc, unsigned iRetType, FLOATUNION *pRet)
505{
506 /*
507 * Skip to the end of long form?
508 */
509 char ch;
510 if ( cchMax >= 5
511 && ((ch = psz[0]) == 'i' || ch == 'I')
512 && ((ch = psz[1]) == 'n' || ch == 'N')
513 && ((ch = psz[2]) == 'i' || ch == 'I')
514 && ((ch = psz[3]) == 't' || ch == 'T')
515 && ((ch = psz[4]) == 'y' || ch == 'Y'))
516 {
517 psz += 5;
518 cchMax -= 5;
519 }
520
521 /*
522 * Set the return value:
523 */
524 switch (iRetType)
525 {
526 case RET_TYPE_FLOAT:
527 {
528 RTFLOAT32U const uRet = RTFLOAT32U_INIT_INF(!fPositive);
529 AssertCompile(sizeof(uRet) == sizeof(pRet->r.r));
530 pRet->r.r = uRet.r;
531 break;
532 }
533
534 case RET_TYPE_LONG_DOUBLE:
535#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
536 {
537 RTFLOAT80U2 const uRet = RTFLOAT80U_INIT_INF(!fPositive);
538 pRet->lrd.lrd = uRet.lrd;
539 break;
540 }
541#else
542 AssertCompile(sizeof(long double) == sizeof(pRet->rd.rd));
543 RT_FALL_THRU();
544#endif
545 case RET_TYPE_DOUBLE:
546 {
547 RTFLOAT64U const uRet = RTFLOAT64U_INIT_INF(!fPositive);
548 AssertCompile(sizeof(uRet) == sizeof(pRet->rd.rd));
549 pRet->rd.rd = uRet.rd;
550 break;
551 }
552
553 default: AssertFailedBreak();
554 }
555
556 /*
557 * Deal with whatever follows and return:
558 */
559 return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, rc);
560}
561
562
563/**
564 * Parses the tag of a "NaN(tag)" value.
565 *
566 * We take the tag to be a number to be put in the mantissa of the NaN, possibly
567 * prefixed by 'quiet_' or 'silent_' (all or part) to indicate the type of NaN.
568 *
569 * @returns Value found in the tag string. Caller must mask it to fit in the
570 * target format.
571 * @param pchTag The tag string to parse. Not zero terminated.
572 * @param cchTag The length of the tag string value.
573 * @param pfQuiet Where to return the type of NaN. Default is quiet.
574 */
575static uint64_t rtStrParseNanTag(const char *pchTag, size_t cchTag, bool *pfQuiet)
576{
577 *pfQuiet = true;
578
579 /*
580 * Skip 0x - content is hexadecimal, so this is not necessary.
581 */
582 if (cchTag > 2 && pchTag[0] == '0' && (pchTag[1] == 'x' || pchTag[1] == 'X'))
583 {
584 pchTag += 2;
585 cchTag -= 2;
586 }
587
588 /*
589 * Parse the number, ignoring overflows and stopping on non-xdigit.
590 */
591 uint64_t uRet = 0;
592 while (cchTag > 0)
593 {
594 unsigned char uch = (unsigned char)*pchTag;
595 unsigned char uchDigit = g_auchDigits[uch];
596 if (uchDigit >= 16)
597 break;
598 uRet *= 16;
599 uRet += uchDigit;
600 pchTag++;
601 cchTag--;
602 }
603
604 /*
605 * Check for special "non-standard" quiet / signalling indicator.
606 */
607 while (cchTag > 0 && *pchTag == '_')
608 pchTag++, cchTag--;
609 if (cchTag > 0)
610 {
611 char const ch = pchTag[0];
612 if (ch == 'q' || ch == 'Q')
613 *pfQuiet = true;
614 else if (ch == 's' || ch == 'S')
615 *pfQuiet = false;
616 }
617
618 return uRet;
619}
620
621
622/**
623 * Finish parsing NaN, set @a pRet to NaN, set @a ppszNext, and check for
624 * trailing spaces & chars if @a rc is VINF_SUCCESS.
625 *
626 * @returns IPRT status code.
627 * @param psz The current input position.
628 * @param ppszNext Where to return the pointer to the end of the value.
629 * Optional.
630 * @param cchMax Number of bytes left in the string starting at @a psz.
631 * @param rc The status code to return.
632 */
633static int rtStrToLongDoubleReturnNan(const char *psz, char **ppszNext, size_t cchMax, bool fPositive,
634 unsigned iRetType, FLOATUNION *pRet)
635{
636 /*
637 * Any NaN sub-number? E.g. NaN(1) or Nan(0x42). We'll require a closing
638 * parenthesis or we'll just ignore it.
639 */
640 bool fQuiet = true;
641 uint64_t uNum = 1;
642 if (cchMax >= 2 && *psz == '(')
643 {
644 unsigned cch = 1;
645 char ch = '\0';
646 while (cch < cchMax && (RT_C_IS_ALNUM((ch = psz[cch])) || ch == '_'))
647 cch++;
648 if (ch == ')')
649 {
650 uNum = rtStrParseNanTag(psz + 1, cch - 1, &fQuiet);
651 psz += cch + 1;
652 cchMax -= cch + 1;
653
654 Assert(iRetType < RT_ELEMENTS(g_fNanMasks));
655 uNum &= g_fNanMasks[iRetType];
656 if (!uNum)
657 uNum = 1; /* must not be zero, or it'll turn into an infinity */
658 }
659 }
660
661 /*
662 * Set the return value.
663 */
664 switch (iRetType)
665 {
666 case RET_TYPE_FLOAT:
667 {
668 RTFLOAT32U const uRet = RTFLOAT32U_INIT_NAN_EX(fQuiet, !fPositive, (uint32_t)uNum);
669 pRet->r = uRet;
670 break;
671 }
672
673 case RET_TYPE_LONG_DOUBLE:
674#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
675 {
676 RTFLOAT80U2 const uRet = RTFLOAT80U_INIT_NAN_EX(fQuiet, !fPositive, uNum);
677 pRet->lrd = uRet;
678 break;
679 }
680#else
681 AssertCompile(sizeof(long double) == sizeof(pRet->rd.rd));
682 RT_FALL_THRU();
683#endif
684 case RET_TYPE_DOUBLE:
685 {
686 RTFLOAT64U const uRet = RTFLOAT64U_INIT_NAN_EX(fQuiet, !fPositive, uNum);
687 pRet->rd = uRet;
688 break;
689 }
690
691 default: AssertFailedBreak();
692 }
693
694 return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, VINF_SUCCESS);
695}
696
697
698/**
699 * Set @a pRet to zero, set @a ppszNext, and check for trailing spaces &
700 * chars if @a rc is VINF_SUCCESS.
701 *
702 * @returns IPRT status code.
703 * @param psz The current input position.
704 * @param ppszNext Where to return the pointer to the end of the value.
705 * Optional.
706 * @param cchMax Number of bytes left in the string starting at @a psz.
707 * @param rc The status code to return.
708 */
709static int rtStrToLongDoubleReturnZero(const char *psz, char **ppszNext, size_t cchMax, bool fPositive,
710 int rc, unsigned iRetType, FLOATUNION *pRet)
711{
712 switch (iRetType)
713 {
714 case RET_TYPE_FLOAT:
715 pRet->r.r = fPositive ? +0.0F : -0.0F;
716 break;
717
718 case RET_TYPE_LONG_DOUBLE:
719#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
720 pRet->lrd.lrd = fPositive ? +0.0L : -0.0L;
721 break;
722#else
723 AssertCompile(sizeof(long double) == sizeof(pRet->rd.rd));
724 RT_FALL_THRU();
725#endif
726 case RET_TYPE_DOUBLE:
727 pRet->rd.rd = fPositive ? +0.0 : -0.0;
728 break;
729
730 default: AssertFailedBreak();
731 }
732
733 return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, rc);
734}
735
736
737/**
738 * Return overflow or underflow - setting @a pRet and @a ppszNext accordingly.
739 *
740 * @returns IPRT status code.
741 * @param psz The current input position.
742 * @param ppszNext Where to return the pointer to the end of the value.
743 * Optional.
744 * @param cchMax Number of bytes left in the string starting at @a psz.
745 * @param rc The status code to return.
746 */
747static int rtStrToLongDoubleReturnOverflow(const char *psz, char **ppszNext, size_t cchMax, bool fPositive,
748 int32_t iExponent, unsigned iRetType, FLOATUNION *pRet)
749{
750 if (iExponent > 0)
751 return rtStrToLongDoubleReturnInf(psz, ppszNext, cchMax, fPositive, VERR_FLOAT_OVERFLOW, iRetType, pRet);
752 return rtStrToLongDoubleReturnZero(psz, ppszNext, cchMax, fPositive, VERR_FLOAT_UNDERFLOW, iRetType, pRet);
753}
754
755
756/**
757 * Returns a denormal/subnormal value.
758 *
759 * This implies that iRetType is long double, or double if they are the same,
760 * and that we should warn about underflowing.
761 */
762static int rtStrToLongDoubleReturnSubnormal(const char *psz, char **ppszNext, size_t cchMax, LONG_DOUBLE_U_T const *pVal,
763 unsigned iRetType, FLOATUNION *pRet)
764{
765#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
766 Assert(iRetType == RET_TYPE_LONG_DOUBLE);
767 pRet->lrd = *pVal;
768#else
769 Assert(iRetType == RET_TYPE_LONG_DOUBLE || iRetType == RET_TYPE_DOUBLE);
770 pRet->rd = *pVal;
771#endif
772 RT_NOREF(iRetType);
773 return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, VWRN_FLOAT_UNDERFLOW);
774}
775
776
777/**
778 * Packs the given sign, mantissa, and (power of 2) exponent into the
779 * return value.
780 */
781static int rtStrToLongDoubleReturnValue(const char *psz, char **ppszNext, size_t cchMax,
782 bool fPositive, uint64_t uMantissa, int32_t iExponent,
783 unsigned iRetType, FLOATUNION *pRet)
784{
785 int rc = VINF_SUCCESS;
786 switch (iRetType)
787 {
788 case RET_TYPE_FLOAT:
789 iExponent += RTFLOAT32U_EXP_BIAS;
790 if (iExponent <= 0)
791 {
792 /* Produce a subnormal value if it's within range, otherwise return zero. */
793 if (iExponent < -RTFLOAT32U_FRACTION_BITS)
794 return rtStrToLongDoubleReturnZero(psz, ppszNext, cchMax, fPositive, VWRN_FLOAT_UNDERFLOW, iRetType, pRet);
795 rc = VWRN_FLOAT_UNDERFLOW;
796 uMantissa >>= -iExponent + 1;
797 iExponent = 0;
798 }
799 else if (iExponent >= RTFLOAT32U_EXP_MAX)
800 return rtStrToLongDoubleReturnInf(psz, ppszNext, cchMax, fPositive, VWRN_FLOAT_OVERFLOW, iRetType, pRet);
801
802 pRet->r.s.uFraction = (uMantissa >> (63 - RTFLOAT32U_FRACTION_BITS)) & (RT_BIT_64(RTFLOAT32U_FRACTION_BITS) - 1);
803 pRet->r.s.uExponent = iExponent;
804 pRet->r.s.fSign = !fPositive;
805 break;
806
807 case RET_TYPE_LONG_DOUBLE:
808#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
809 iExponent += RTFLOAT80U_EXP_BIAS;
810 if (iExponent <= 0)
811 {
812 /* Produce a subnormal value if it's within range, otherwise return zero. */
813 if (iExponent < -RTFLOAT80U_FRACTION_BITS)
814 return rtStrToLongDoubleReturnZero(psz, ppszNext, cchMax, fPositive, VWRN_FLOAT_UNDERFLOW, iRetType, pRet);
815 rc = VWRN_FLOAT_UNDERFLOW;
816 uMantissa >>= -iExponent + 1;
817 iExponent = 0;
818 }
819 else if (iExponent >= RTFLOAT80U_EXP_MAX)
820 return rtStrToLongDoubleReturnInf(psz, ppszNext, cchMax, fPositive, VWRN_FLOAT_OVERFLOW, iRetType, pRet);
821
822 pRet->lrd.s.uMantissa = uMantissa;
823 pRet->lrd.s.uExponent = iExponent;
824 pRet->lrd.s.fSign = !fPositive;
825 break;
826#else
827 AssertCompile(sizeof(long double) == sizeof(pRet->rd.rd));
828 RT_FALL_THRU();
829#endif
830 case RET_TYPE_DOUBLE:
831 iExponent += RTFLOAT64U_EXP_BIAS;
832 if (iExponent <= 0)
833 {
834 /* Produce a subnormal value if it's within range, otherwise return zero. */
835 if (iExponent < -RTFLOAT64U_FRACTION_BITS)
836 return rtStrToLongDoubleReturnZero(psz, ppszNext, cchMax, fPositive, VWRN_FLOAT_UNDERFLOW, iRetType, pRet);
837 rc = VWRN_FLOAT_UNDERFLOW;
838 uMantissa >>= -iExponent + 1;
839 iExponent = 0;
840 }
841 else if (iExponent >= RTFLOAT64U_EXP_MAX)
842 return rtStrToLongDoubleReturnInf(psz, ppszNext, cchMax, fPositive, VWRN_FLOAT_OVERFLOW, iRetType, pRet);
843
844 pRet->rd.s64.uFraction = (uMantissa >> (63 - RTFLOAT64U_FRACTION_BITS)) & (RT_BIT_64(RTFLOAT64U_FRACTION_BITS) - 1);
845 pRet->rd.s64.uExponent = iExponent;
846 pRet->rd.s64.fSign = !fPositive;
847 break;
848
849 default:
850 AssertFailedReturn(VERR_INTERNAL_ERROR_3);
851 }
852 return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, rc);
853}
854
855
856/**
857 * Worker for RTStrToLongDoubleEx, RTStrToDoubleEx and RTStrToFloatEx.
858 *
859 * @returns IPRT status code
860 * @param pszValue The string value to convert.
861 * @param ppszNext Where to return the pointer to the end of the value.
862 * Optional.
863 * @param cchMax Number of bytes left in the string starting at @a psz.
864 * @param iRetType The return type: float, double or long double.
865 * @param pRet The return value union.
866 */
867static int rtStrToLongDoubleWorker(const char *pszValue, char **ppszNext, size_t cchMax, unsigned iRetType, FLOATUNION *pRet)
868{
869 const char *psz = pszValue;
870 if (!cchMax)
871 cchMax = ~(size_t)cchMax;
872
873 /*
874 * Sign.
875 */
876 bool fPositive = true;
877 while (cchMax > 0)
878 {
879 if (*psz == '+')
880 fPositive = true;
881 else if (*psz == '-')
882 fPositive = !fPositive;
883 else
884 break;
885 psz++;
886 cchMax--;
887 }
888
889 /*
890 * Constant like "Inf", "Infinity", "NaN" or "NaN(hexstr)"?
891 */
892 /* "Inf" or "Infinity"? */
893 if (cchMax == 0)
894 return rtStrToLongDoubleReturnZero(pszValue, ppszNext, cchMax, fPositive, VERR_NO_DIGITS, iRetType, pRet);
895 if (cchMax >= 3)
896 {
897 char ch = *psz;
898 /* Inf: */
899 if (ch == 'i' || ch == 'I')
900 {
901 if ( ((ch = psz[1]) == 'n' || ch == 'N')
902 && ((ch = psz[2]) == 'f' || ch == 'F'))
903 return rtStrToLongDoubleReturnInf(psz + 3, ppszNext, cchMax - 3, fPositive, VINF_SUCCESS, iRetType, pRet);
904 }
905 /* Nan: */
906 else if (ch == 'n' || ch == 'N')
907 {
908 if ( ((ch = psz[1]) == 'a' || ch == 'A')
909 && ((ch = psz[2]) == 'n' || ch == 'N'))
910 return rtStrToLongDoubleReturnNan(psz + 3, ppszNext, cchMax - 3, fPositive, iRetType, pRet);
911 }
912 }
913
914 /*
915 * Check for hex prefix.
916 */
917#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
918 unsigned cMaxDigits = 19;
919#else
920 unsigned cMaxDigits = 18;
921#endif
922 unsigned uBase = 10;
923 unsigned uExpDigitFactor = 1;
924 if (cchMax >= 2 && psz[0] == '0' && (psz[1] == 'x' || psz[1] == 'X'))
925 {
926 cMaxDigits = 16;
927 uBase = 16;
928 uExpDigitFactor = 4;
929 cchMax -= 2;
930 psz += 2;
931 }
932
933 /*
934 * Now, parse the mantissa.
935 */
936 uint8_t abDigits[20];
937 unsigned cDigits = 0;
938 unsigned cFractionDigits = 0;
939 uint8_t fSeenNonZeroDigit = 0;
940 bool fInFraction = false;
941 bool fSeenDigits = false;
942 while (cchMax > 0)
943 {
944 uint8_t b = g_auchDigits[(unsigned char)*psz];
945 if (b < uBase)
946 {
947 fSeenDigits = true;
948 fSeenNonZeroDigit |= b;
949 if (fSeenNonZeroDigit)
950 {
951 if (cDigits < RT_ELEMENTS(abDigits))
952 abDigits[cDigits] = b;
953 cDigits++;
954 cFractionDigits += fInFraction;
955 }
956 }
957 else if (b == DIGITS_DOT && !fInFraction)
958 fInFraction = true;
959 else
960 break;
961 psz++;
962 cchMax--;
963 }
964
965 /* If we've seen no digits, or just a dot, return zero already. */
966 if (!fSeenDigits)
967 {
968 if (fInFraction) /* '+.' => 0.0 ? */
969 return rtStrToLongDoubleReturnZero(psz, ppszNext, cchMax, fPositive, VINF_SUCCESS, iRetType, pRet);
970 if (uBase == 16) /* '+0x' => 0.0 & *=pszNext="x..." */
971 return rtStrToLongDoubleReturnZero(psz - 1, ppszNext, cchMax, fPositive, VINF_SUCCESS, iRetType, pRet);
972 /* '' and '+' -> no digits + 0.0. */
973 return rtStrToLongDoubleReturnZero(pszValue, ppszNext, cchMax, fPositive, VERR_NO_DIGITS, iRetType, pRet);
974 }
975
976 /*
977 * Parse the exponent.
978 * This is optional and we ignore incomplete ones like "e+".
979 */
980 int32_t iExponent = 0;
981 if (cchMax >= 2) /* min "e0" */
982 {
983 char ch = *psz;
984 if (uBase == 10 ? ch == 'e' || ch == 'E' : ch == 'p' || ch == 'P')
985 {
986 bool fExpOverflow = false;
987 bool fPositiveExp = true;
988 size_t off = 1;
989 ch = psz[off];
990 if (ch == '+' || ch == '-')
991 {
992 fPositiveExp = ch == '+';
993 off++;
994 }
995 uint8_t b;
996 if ( off < cchMax
997 && (b = g_auchDigits[(unsigned char)psz[off]]) < 10)
998 {
999 do
1000 {
1001 int32_t const iPreviousExponent = iExponent;
1002 iExponent *= 10;
1003 iExponent += b;
1004 if (iExponent < iPreviousExponent)
1005 fExpOverflow = true;
1006 off++;
1007 } while (off < cchMax && (b = g_auchDigits[(unsigned char)psz[off]]) < 10);
1008 if (!fPositiveExp)
1009 iExponent = -iExponent;
1010 cchMax -= off;
1011 psz += off;
1012 }
1013 if (fExpOverflow || iExponent <= -65536 || iExponent >= 65536)
1014 return rtStrToLongDoubleReturnOverflow(pszValue, ppszNext, cchMax, fPositive, iExponent, iRetType, pRet);
1015 }
1016 }
1017
1018 /* If the mantissa was all zeros, we can return zero now that we're past the exponent. */
1019 if (!fSeenNonZeroDigit)
1020 return rtStrToLongDoubleReturnZero(psz, ppszNext, cchMax, fPositive, VINF_SUCCESS, iRetType, pRet);
1021
1022 /*
1023 * Adjust the expontent so we've got all digits to the left of the decimal point.
1024 */
1025 iExponent -= cFractionDigits * uExpDigitFactor;
1026
1027 /*
1028 * Drop digits we won't translate.
1029 */
1030 if (cDigits > cMaxDigits)
1031 {
1032 iExponent += (cDigits - cMaxDigits) * uExpDigitFactor;
1033 cDigits = cMaxDigits;
1034 }
1035
1036 /*
1037 * Strip least significant zero digits.
1038 */
1039 while (cDigits > 0 && abDigits[cDigits - 1] == 0)
1040 {
1041 cDigits--;
1042 iExponent += uExpDigitFactor;
1043 }
1044
1045 /*
1046 * The hexadecimal is relatively straight forward.
1047 */
1048 if (uBase == 16)
1049 {
1050 uint64_t uMantissa = 0;
1051 for (unsigned iDigit = 0; iDigit < cDigits; iDigit++)
1052 {
1053 uMantissa |= (uint64_t)abDigits[iDigit] << (64 - 4 - iDigit * 4);
1054 iExponent += 4;
1055 }
1056 Assert(uMantissa != 0);
1057
1058 /* Shift to the left till the most significant bit is 1. */
1059 if (!(uMantissa & RT_BIT_64(63)))
1060 {
1061 unsigned cShift = 64 - ASMBitLastSetU64(uMantissa);
1062 uMantissa <<= cShift;
1063 iExponent -= cShift;
1064 Assert(uMantissa & RT_BIT_64(63));
1065 }
1066
1067 /* Account for the 1 left of the decimal point. */
1068 iExponent--;
1069
1070 /*
1071 * Produce the return value.
1072 */
1073 return rtStrToLongDoubleReturnValue(psz, ppszNext, cchMax, fPositive, uMantissa, iExponent, iRetType, pRet);
1074 }
1075
1076 /*
1077 * For the decimal format, we'll rely on the floating point conversion of
1078 * the compiler/CPU for the mantissa.
1079 */
1080 uint64_t uMantissa = 0;
1081 for (unsigned iDigit = 0; iDigit < cDigits; iDigit++)
1082 {
1083 uMantissa *= 10;
1084 uMantissa += abDigits[iDigit];
1085 }
1086 Assert(uMantissa != 0);
1087
1088 LONG_DOUBLE_U_T uTmp;
1089 uTmp.r = fPositive ? (long double)uMantissa : -(long double)uMantissa;
1090
1091 /*
1092 * Here comes the fun part, scaling it according to the power of 10 exponent.
1093 * We only need to consider overflows and underflows when scaling, when
1094 * iExponent is zero we can be sure the target type can handle the result.
1095 */
1096 if (iExponent != 0)
1097 {
1098 rtStrToLongDoubleExp10(&uTmp, iExponent);
1099#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
1100 if (!RTFLOAT80U_IS_NORMAL(&uTmp))
1101#else
1102 if (!RTFLOAT64U_IS_NORMAL(&uTmp))
1103#endif
1104 {
1105#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
1106 if (RTFLOAT80U_IS_DENORMAL(&uTmp) && iRetType == RET_TYPE_LONG_DOUBLE)
1107#else
1108 if (RTFLOAT64U_IS_SUBNORMAL(&uTmp) && iRetType != RET_TYPE_FLOAT)
1109#endif
1110 return rtStrToLongDoubleReturnSubnormal(psz, ppszNext, cchMax, &uTmp, iRetType, pRet);
1111 return rtStrToLongDoubleReturnOverflow(psz, ppszNext, cchMax, fPositive, iExponent, iRetType, pRet);
1112 }
1113 }
1114
1115 /*
1116 * We've got a normal value in uTmp when we get here, just repack it in the
1117 * target format and return.
1118 */
1119#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
1120 Assert(RTFLOAT80U_IS_NORMAL(&uTmp));
1121 if (iRetType == RET_TYPE_LONG_DOUBLE)
1122 {
1123 pRet->lrd = uTmp;
1124 return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, VINF_SUCCESS);
1125 }
1126 fPositive = uTmp.s.fSign;
1127 iExponent = uTmp.s.uExponent - RTFLOAT80U_EXP_BIAS;
1128 uMantissa = uTmp.s.uMantissa;
1129#else
1130 Assert(RTFLOAT64U_IS_NORMAL(&uTmp));
1131 if ( iRetType == RET_TYPE_DOUBLE
1132 || iRetType == RET_TYPE_LONG_DOUBLE)
1133 {
1134 pRet->rd = uTmp;
1135 return rtStrToLongDoubleReturnChecks(psz, ppszNext, cchMax, VINF_SUCCESS);
1136 }
1137 fPositive = uTmp.s64.fSign;
1138 iExponent = uTmp.s64.uExponent - RTFLOAT64U_EXP_BIAS;
1139 uMantissa = uTmp.s64.uFraction | RT_BIT_64(RTFLOAT64U_FRACTION_BITS);
1140#endif
1141 return rtStrToLongDoubleReturnValue(psz, ppszNext, cchMax, fPositive, uMantissa, iExponent, iRetType, pRet);
1142}
1143
1144
1145RTDECL(int) RTStrToLongDoubleEx(const char *pszValue, char **ppszNext, size_t cchMax, long double *plrd)
1146{
1147 FLOATUNION u;
1148 int rc = rtStrToLongDoubleWorker(pszValue, ppszNext, cchMax, RET_TYPE_LONG_DOUBLE, &u);
1149 if (plrd)
1150#ifdef RT_COMPILER_WITH_80BIT_LONG_DOUBLE
1151 *plrd = u.lrd.lrd;
1152#else
1153 *plrd = u.rd.rd;
1154#endif
1155 return rc;
1156}
1157
1158
1159RTDECL(int) RTStrToDoubleEx(const char *pszValue, char **ppszNext, size_t cchMax, double *prd)
1160{
1161 FLOATUNION u;
1162 int rc = rtStrToLongDoubleWorker(pszValue, ppszNext, cchMax, RET_TYPE_DOUBLE, &u);
1163 if (prd)
1164 *prd = u.rd.rd;
1165 return rc;
1166}
1167
1168
1169RTDECL(int) RTStrToFloatEx(const char *pszValue, char **ppszNext, size_t cchMax, float *pr)
1170{
1171 FLOATUNION u;
1172 int rc = rtStrToLongDoubleWorker(pszValue, ppszNext, cchMax, RET_TYPE_FLOAT, &u);
1173 if (pr)
1174 *pr = u.r.r;
1175 return rc;
1176}
1177
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette