/src/igraph/src/core/printing.c
Line | Count | Source |
1 | | /* |
2 | | igraph library. |
3 | | Copyright (C) 2012 Gabor Csardi <csardi.gabor@gmail.com> |
4 | | 334 Harvard street, Cambridge, MA 02139 USA |
5 | | |
6 | | This program is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 2 of the License, or |
9 | | (at your option) any later version. |
10 | | |
11 | | This program is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with this program; if not, write to the Free Software |
18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
19 | | 02110-1301 USA |
20 | | |
21 | | */ |
22 | | |
23 | | #include "igraph_complex.h" |
24 | | #include "igraph_error.h" |
25 | | #include "igraph_types.h" |
26 | | |
27 | | #include <float.h> |
28 | | |
29 | | /* The number of digits chosen here will be used in all places where |
30 | | * igraph_real_fprintf_precise() is used, including all textual graph |
31 | | * formats such as GML, GraphML, Pajek, etc. DBL_DIG digits are sufficient |
32 | | * to preserve the decimal representation during a |
33 | | * decimal (textual) -> binary -> decimal (textual) round-trip conversion. |
34 | | * This many digits are however not sufficient for a lossless |
35 | | * binary -> decimal -> binary conversion. Thus, writing numerical attributes |
36 | | * to a file and reading them back in may cause a tiny change in the last |
37 | | * binary digit of numbers. This change is minute, always smaller than 10^-15 |
38 | | * times the original number, thus acceptable. |
39 | | * |
40 | | * We could output more digits, but that would come with its own problem: |
41 | | * It would sometimes cause a change in the decimal representation originally |
42 | | * input by users, which is surprising and confusing. For example, |
43 | | * |
44 | | * printf("%.17g\n", 100.1) |
45 | | * |
46 | | * outputs 100.09999999999999 instead of 100.1. We can prevent this by |
47 | | * using DBL_DIG == 15 digits instead of 17, which would be required |
48 | | * for a lossless binary -> decimal -> binary round-tripping. |
49 | | * |
50 | | * This justifies using DBL_DIG digits, and not more, in all places. |
51 | | */ |
52 | | #ifdef DBL_DIG |
53 | | /* Use DBL_DIG to determine the maximum precision used for %g */ |
54 | 6.28M | #define IGRAPH_REAL_PRINTF_PRECISE_FORMAT "%." IGRAPH_I_STRINGIFY(DBL_DIG) "g" |
55 | | #else |
56 | | /* Assume a precision of 15 digits for %g, which is what IEEE-754 doubles require. */ |
57 | | #define IGRAPH_REAL_PRINTF_PRECISE_FORMAT "%.15g" |
58 | | #endif |
59 | | |
60 | 0 | int igraph_real_fprintf(FILE *file, igraph_real_t val) { |
61 | 0 | if (isfinite(val)) { |
62 | 0 | return fprintf(file, "%g", val); |
63 | 0 | } else if (isnan(val)) { |
64 | 0 | return fprintf(file, "NaN"); |
65 | 0 | } else if (isinf(val)) { |
66 | 0 | if (val < 0) { |
67 | 0 | return fprintf(file, "-Inf"); |
68 | 0 | } else { |
69 | 0 | return fprintf(file, "Inf"); |
70 | 0 | } |
71 | 0 | } |
72 | 0 | IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ |
73 | 0 | } |
74 | | |
75 | | #ifndef USING_R |
76 | 0 | int igraph_real_printf(igraph_real_t val) { |
77 | 0 | return igraph_real_fprintf(stdout, val); |
78 | 0 | } |
79 | | #endif |
80 | | |
81 | 0 | int igraph_real_fprintf_aligned(FILE *file, int width, igraph_real_t val) { |
82 | 0 | if (isfinite(val)) { |
83 | 0 | return fprintf(file, "%*g", width, val); |
84 | 0 | } else if (isnan(val)) { |
85 | 0 | return fprintf(file, "%*s", width, "NaN"); |
86 | 0 | } else if (isinf(val)) { |
87 | 0 | if (val < 0) { |
88 | 0 | return fprintf(file, "%*s", width, "-Inf"); |
89 | 0 | } else { |
90 | 0 | return fprintf(file, "%*s", width, "Inf"); |
91 | 0 | } |
92 | 0 | } |
93 | 0 | IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ |
94 | 0 | } |
95 | | |
96 | | #ifndef USING_R |
97 | 0 | int igraph_real_printf_aligned(int width, igraph_real_t val) { |
98 | 0 | return igraph_real_fprintf_aligned(stdout, width, val); |
99 | 0 | } |
100 | | #endif |
101 | | |
102 | 0 | int igraph_real_snprintf(char *str, size_t size, igraph_real_t val) { |
103 | 0 | if (isfinite(val)) { |
104 | 0 | return snprintf(str, size, "%g", val); |
105 | 0 | } else if (isnan(val)) { |
106 | 0 | return snprintf(str, size, "NaN"); |
107 | 0 | } else if (isinf(val)) { |
108 | 0 | if (val < 0) { |
109 | 0 | return snprintf(str, size, "-Inf"); |
110 | 0 | } else { |
111 | 0 | return snprintf(str, size, "Inf"); |
112 | 0 | } |
113 | 0 | } |
114 | 0 | IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ |
115 | 0 | } |
116 | | |
117 | 10.1M | int igraph_real_fprintf_precise(FILE *file, igraph_real_t val) { |
118 | 10.1M | if (isfinite(val)) { |
119 | 6.24M | return fprintf(file, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); |
120 | 6.24M | } else if (isnan(val)) { |
121 | 3.84M | return fprintf(file, "NaN"); |
122 | 3.84M | } else if (isinf(val)) { |
123 | 31.6k | if (val < 0) { |
124 | 30.0k | return fprintf(file, "-Inf"); |
125 | 30.0k | } else { |
126 | 1.60k | return fprintf(file, "Inf"); |
127 | 1.60k | } |
128 | 31.6k | } |
129 | 0 | IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ |
130 | 0 | } |
131 | | |
132 | | #ifndef USING_R |
133 | 0 | int igraph_real_printf_precise(igraph_real_t val) { |
134 | 0 | return igraph_real_fprintf_precise(stdout, val); |
135 | 0 | } |
136 | | #endif |
137 | | |
138 | 12.6M | int igraph_real_snprintf_precise(char *str, size_t size, igraph_real_t val) { |
139 | 12.6M | if (isfinite(val)) { |
140 | 37.7k | return snprintf(str, size, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); |
141 | 12.6M | } else if (isnan(val)) { |
142 | 12.5M | return snprintf(str, size, "NaN"); |
143 | 12.5M | } else if (isinf(val)) { |
144 | 30.4k | if (val < 0) { |
145 | 29.5k | return snprintf(str, size, "-Inf"); |
146 | 29.5k | } else { |
147 | 948 | return snprintf(str, size, "Inf"); |
148 | 948 | } |
149 | 30.4k | } |
150 | 0 | IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ |
151 | 0 | } |
152 | | |
153 | | #define PROPAGATE() \ |
154 | 0 | do { \ |
155 | 0 | if (res < 0) { \ |
156 | 0 | return -1; \ |
157 | 0 | } \ |
158 | 0 | cnt += res; \ |
159 | 0 | } while (0) |
160 | | |
161 | 0 | int igraph_complex_fprintf(FILE *file, igraph_complex_t val) { |
162 | 0 | int res, cnt = 0; |
163 | 0 | igraph_real_t re = IGRAPH_REAL(val), im = IGRAPH_IMAG(val); |
164 | 0 | res = igraph_real_fprintf(file, re); |
165 | 0 | PROPAGATE(); |
166 | 0 | if (! signbit(im)) { |
167 | 0 | res = fprintf(file, "+"); |
168 | 0 | PROPAGATE(); |
169 | 0 | } |
170 | 0 | res = igraph_real_fprintf(file, im); |
171 | 0 | PROPAGATE(); |
172 | 0 | res = fprintf(file, "i"); |
173 | 0 | PROPAGATE(); |
174 | 0 | return cnt; |
175 | 0 | } |
176 | | |
177 | | #undef PROPAGATE |
178 | | |
179 | | #ifndef USING_R |
180 | 0 | int igraph_complex_printf(igraph_complex_t val) { |
181 | 0 | return igraph_complex_fprintf(stdout, val); |
182 | 0 | } |
183 | | #endif |
184 | | |
185 | | #define PROPAGATE() \ |
186 | 0 | do { \ |
187 | 0 | if (res < 0) { \ |
188 | 0 | return -1; \ |
189 | 0 | } \ |
190 | 0 | cnt += res; \ |
191 | 0 | /* remember that 'size' is unsigned, can't check if size - res < 0! */ \ |
192 | 0 | if (size > (size_t) res) size -= res; \ |
193 | 0 | else size = 0; \ |
194 | 0 | if (size == 0) str = NULL; else str += res; \ |
195 | 0 | } while (0) |
196 | | |
197 | 0 | int igraph_complex_snprintf(char *str, size_t size, igraph_complex_t val) { |
198 | 0 | int res, cnt = 0; |
199 | 0 | igraph_real_t re = IGRAPH_REAL(val), im = IGRAPH_IMAG(val); |
200 | 0 | res = igraph_real_snprintf(str, size, re); |
201 | 0 | PROPAGATE(); |
202 | 0 | if (! signbit(im)) { |
203 | 0 | res = snprintf(str, size, "+"); |
204 | 0 | PROPAGATE(); |
205 | 0 | } |
206 | 0 | res = igraph_real_snprintf(str, size, im); |
207 | 0 | PROPAGATE(); |
208 | 0 | res = snprintf(str, size, "i"); |
209 | 0 | PROPAGATE(); |
210 | 0 | return cnt; |
211 | 0 | } |
212 | | |
213 | 0 | int igraph_complex_fprintf_aligned(FILE *file, int width, igraph_complex_t val) { |
214 | | /* Most characters produces by %g is 13, so including 'i' and null terminator we |
215 | | * need up to 13 + 13 + 1 + 1 = 28 characters in total. */ |
216 | 0 | char buf[28]; |
217 | |
|
218 | 0 | if (igraph_complex_snprintf(buf, sizeof(buf) / sizeof(buf[0]), val) < 0) { |
219 | 0 | return -1; |
220 | 0 | } |
221 | 0 | return fprintf(file, "%*s", width, buf); |
222 | 0 | } |
223 | | |
224 | | #ifndef USING_R |
225 | 0 | int igraph_complex_printf_aligned(int width, igraph_complex_t val) { |
226 | | return igraph_complex_fprintf_aligned(stdout, width, val); |
227 | 0 | } |
228 | | #endif |
229 | | |
230 | | #undef PROPAGATE |