1
  
2
  
3
  
4
  
5
  
6
  
7
  
8
  
9
  
10
  
11
  
12
  
13
  
14
  
15
  
16
  
17
  
18
  
19
  
20
  
21
  
22
  
23
  
24
  
25
  
26
  
27
  
28
  
29
  
30
  
31
  
32
  
33
  
34
  
35
  
36
  
37
  
38
  
39
  
40
  
41
  
42
  
43
  
44
  
45
  
46
  
47
  
48
  
49
  
50
  
51
  
52
  
53
  
54
  
55
  
56
  
57
  
58
  
59
  
60
  
61
  
62
  
63
  
64
  
65
  
66
  
67
  
68
  
69
  
70
  
71
  
72
  
73
  
74
  
75
  
76
  
77
  
78
  
79
  
80
  
81
  
82
  
83
  
84
  
85
  
86
  
87
  
88
  
89
  
/* 
|| This file is part of Pike. For copyright information see COPYRIGHT. 
|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING 
|| for more information. 
*/ 
 
/* Misc stuff for dealing with floats. 
 */ 
 
#include "pike_float.h" 
#include <stdio.h> 
#include <string.h> 
 
PMOD_EXPORT void format_pike_float (char *buf, FLOAT_TYPE f) 
/* Writes a formatted float to the given buffer, which must be at 
 * least MAX_FLOAT_SPRINTF_LEN chars long. 
 * 
 * For normal numbers, the format follows the standard programming 
 * language format, i.e. [-]ddd[.ddd][e┬▒ddd] in base 10, where either 
 * a decimal point or an exponent always is present. The integer part 
 * starts with a zero only if it is zero. 
 * 
 * The result can also be one of "nan", "inf" or "-inf" for those 
 * special numbers. */ 
{ 
  /* There's no good way to do this: 
   * 
   * o  Using sprintf makes us susceptible to locale differences 
   *    (affects the decimal separator, but nothing else). 
   * 
   * o  Temporarily changing the locale is not thread safe. 
   * 
   * o  Rolling an own formatter is not sufficiently trivial, 
   *    considering how complicated the one in glibc is. Even if the 
   *    code is ripped from somewhere, the risk of subtle formatting 
   *    bugs is prohibitive. 
   * 
   * The conclusion is to use sprintf anyway and replace the decimal 
   * separator afterwards. 
   */ 
 
  char *p; 
 
  if (PIKE_ISNAN (f)) { 
    strcpy (buf, "nan"); 
    return; 
  } 
 
  if (PIKE_ISINF (f)) { 
    if (f > 0.0) 
      strcpy (buf, "inf"); 
    else 
      strcpy (buf, "-inf"); 
    return; 
  } 
 
  sprintf (buf, "%.*"PRINTPIKEFLOAT"g", PIKEFLOAT_DIG, f); 
 
  for (p = buf; *p; p++) 
    if (*p != '-' && (*p < '0' || *p > '9')) break; 
 
  if (*p == '.' || *p == 'e') 
    /* Got either a standard decimal point or an exponent. We're done. */ 
    return; 
 
  if (!*p) { 
    /* The number has neither decimal point nor exponent. Add ".0". */ 
    strcpy (p, ".0"); 
    return; 
  } 
 
  /* Got a strange decimal separator. Replace it. */ 
  *p++ = '.'; 
 
  if ((*p >= '0' && *p <= '9') || !*p) 
    return; 
 
  /* Ok, the locale decimal separator is more than one char. This 
   * might seem far out, but there is U+066B ARABIC DECIMAL SEPARATOR, 
   * so we might be looking at a utf-8 sequence. */ 
  { 
    char *s; 
    for (s = p + 1; *s; s++) 
      if (*s >= '0' && *s <= '9') break; 
    memmove (p, s, strlen (s) + 1); 
  } 
}