Coverage Report

Created: 2026-08-11 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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