Fossil SCM

fossil-scm / test / test-msc89-rint-snprintf.c
Blame History Raw 317 lines
1
/*
2
** Test MSVC C89 compatible implementations of the following C99 functions:
3
** - double rint( double x )
4
** Rounds a floating-point value to the nearest integer in floating-point
5
** format.
6
** - int snprintf( char *buffer, size_t count, const char *format, ... )
7
** Writes formatted data to a string.
8
**
9
** NOTE: These implementations aim to provide the main functionality, not
10
** the exact behavior as specified in C99 standard.
11
**
12
** BUILD: cl test-msc98-rint-snprintf.c
13
** gcc test-msc98-rint-snprintf.c -lm ## for reference vs. non-MSVC
14
*/
15
16
#include <stdio.h>
17
#include <stdlib.h>
18
#include <string.h>
19
#include <math.h>
20
#include <limits.h>
21
22
#define TEST_MSC89 1
23
24
#if defined(_MSC_VER)
25
#if (defined(TEST_MSC89) || (_MSC_VER < 1900)) /* before MSVC 2015 */
26
#include <stdarg.h>
27
28
/* NOTE: On truncation, this version of snprintf returns the input count, not
29
** the expected number of chars to fully output the requested format (as
30
** done in the C99 standard implementation). However the truncation test should
31
** still be applicable (nret >= count).
32
*/
33
static __forceinline
34
int c89_snprintf(char *buf, size_t count, const char *fmt, ...){
35
va_list argptr;
36
int n;
37
if( count==0 ) return 0;
38
va_start(argptr, fmt);
39
n = _vsprintf_p(buf, count, fmt, argptr);
40
va_end(argptr);
41
42
/* force zero-termination to avoid some known MSVC bugs */
43
if( count>0 ){
44
buf[count - 1] = '\0';
45
if( n<0 ) n = count;
46
}
47
return n;
48
}
49
50
#if defined(_WIN64)
51
#include <emmintrin.h>
52
#include <limits.h>
53
54
static __forceinline
55
double c89_rint(double v){
56
return ( v<0.0 && v>=-0.5 ? -0.0
57
: ( v!=0 && v>LLONG_MIN && v<LLONG_MAX
58
? _mm_cvtsd_si64(_mm_load_sd(&v)) : v ) ); /* SSE2 */
59
}
60
#else
61
static __forceinline
62
double c89_rint(double v){
63
double rn;
64
__asm
65
{
66
FLD v
67
FRNDINT
68
FSTP rn
69
FWAIT
70
};
71
return rn;
72
}
73
#endif /* _WIN64 */
74
#endif /* (defined(TEST_MSC89) || (_MSC_VER < 1900)) */
75
76
#if (_MSC_VER < 1900) /* before MSVC 2015 */
77
# define snprintf c89_snprintf
78
# define rint c89_rint
79
#else
80
# define HAVE_C99_RINT 1
81
#endif
82
83
#elif !defined(_MSC_VER)
84
# define HAVE_C99_RINT 1
85
# define c89_snprintf snprintf
86
# define c89_rint rint
87
#endif /* defined(_MSC_VER) */
88
89
90
#include <assert.h>
91
92
#define SNPRINTF c89_snprintf
93
#define RINT c89_rint
94
95
int test_rint()
96
{
97
const char *TESTNAME = "rint";
98
const struct test_data {
99
double v, expected;
100
} data[] = { /* round to the nearest or even integer */
101
#ifdef HAVE_C99_RINT
102
{INFINITY,INFINITY},
103
#endif
104
{(double)(LLONG_MAX/10000LL) + 0.7,(double)(LLONG_MAX/10000LL) + 1.},
105
{5.5,6.},{5.4,5.},{5.2,5.},{5.,5.},
106
{4.9,5.},{4.5,4.},{4.4,4.},{4.0,4.},
107
{3.7,4.},{3.5,4.},{3.2,3.},{3.0,3.},
108
{2.7,3.},{2.5,2.},{2.2,2.},{2.0,2.},
109
{1.6,2.},{1.5,2.0},{1.3,1.0},{1.0,1.0},
110
{0.9,1.},{0.8,1.},{0.5,0.},{0.49999999999999994,0.},
111
{0.4,0.},{0.1,0.},{0.,0.}
112
};
113
const size_t ndata = sizeof(data)/sizeof(data[0]);
114
int nfailed = 0;
115
int start = 0, end = ndata;
116
int i;
117
int done = 0;
118
119
/* do two passes over the test data (positives, negatives) */
120
do {
121
double sign = ( start<end ? 1. : -1. );
122
for(i=start; ( start< end ? i<end : i>end ); ( start< end ? ++i : --i )){
123
int passed = 0;
124
double v = sign*data[i].v;
125
double rn = c89_rint(v);
126
double expected = sign*data[i].expected;
127
#ifdef HAVE_C99_RINT
128
{
129
double rint_expected = rint(v);
130
int matched = ( expected==rint_expected );
131
if( !matched ){
132
fprintf(stderr, "E:%s|Expected test data[%d]={%.17lf,%.1lf} does not match the actual rint() value=%.1lf\n",__FUNCTION__,
133
i, v, expected, rint_expected);
134
}
135
assert(matched);
136
expected = rint_expected;
137
}
138
#endif
139
passed = ( rn==expected );
140
fprintf(( passed ? stdout : stderr ),
141
"T:%s|c89_rint(%.17lf)=%.1lf expected=%.1lf\t[%s]\n", TESTNAME,
142
v, rn, expected,
143
( passed ? "PASS" : "FAIL" ));
144
if( !passed ) ++nfailed;
145
}
146
147
if( start<end ){
148
int swap = start;
149
start = end - 1; end = swap - 1;
150
}else{
151
done = 1;
152
}
153
}while( !done );
154
155
if( nfailed ){
156
fprintf(stderr,"T:%s|FAILED %d test\n\n", TESTNAME, nfailed);
157
}else{
158
printf("T:%s|PASSED\n\n", TESTNAME);
159
}
160
161
return nfailed;
162
}
163
164
int test_snprintf()
165
{
166
const char *TESTNAME = "snprintf";
167
int nfailed = 0;
168
#define TEST_BUF_MAXSIZE 256
169
const struct test_data {
170
size_t bufsize;
171
const char *fmt, *expected, *full;
172
} data[] = {
173
{TEST_BUF_MAXSIZE,"snprintf(buf, %d)","snprintf(buf, 17)","snprintf(buf, 17)"},
174
{17,"snprintf(buf, %d)","snprintf(buf, 17","snprintf(buf, 17)"},
175
{2,"snprintf(buf, %d)","s","snprintf(buf, 17)"},
176
{0,"snprintf(buf, %d)","","snprintf(buf, 17)"},
177
};
178
const size_t ndata = sizeof(data)/sizeof(data[0]);
179
char buf[TEST_BUF_MAXSIZE] = {0};
180
int i;
181
182
for(i=0; i<ndata; ++i){
183
int passed = 0;
184
size_t count = data[i].bufsize;
185
const char *fmt = data[i].fmt;
186
const char *full = data[i].full;
187
const char *expected = data[i].expected;
188
const int truncate_expected = ( count<=strlen(full) );
189
const int expected_nret = ( !truncate_expected
190
? strlen(expected) : count );
191
const int expected_zero_at = ( !truncate_expected
192
? expected_nret : expected_nret - 1 );
193
int nret;
194
buf[( count>0 ? count - 1 : 0 )] = '\0';
195
nret = c89_snprintf(buf, count, fmt, strlen(fmt));
196
#ifdef HAVE_C99_RINT
197
{
198
char snprintf_expected[TEST_BUF_MAXSIZE] = {0};
199
int snprintf_expected_nret = snprintf(snprintf_expected, count, fmt, strlen(fmt));
200
int matched = ( strcmp(expected, snprintf_expected)==0 );
201
int matched_nret = ( expected_nret==snprintf_expected_nret );
202
if( !matched ){
203
fprintf(stderr, "E:%s|Expected test data[%d]={'%s','%s'} does not match the actual snprintf() value='%s'\n",__FUNCTION__,
204
i, buf, expected, snprintf_expected);
205
}
206
207
/* NOTE: This implementation of c89_snprintf() on truncation returns
208
** the input count, not the expected number of chars needed to fully
209
** output the requested format. So warn, only ifthe expected nret is
210
** less than the count.
211
*/
212
if( !matched_nret && expected_nret<count ){
213
fprintf(stderr, "W:%s|Expected return value=%d for test data[%d]={'%s','%s'} does not match the actual snprintf() return value=%d\n",__FUNCTION__,
214
expected_nret, i, buf, expected, snprintf_expected_nret);
215
}
216
assert(matched);
217
expected = snprintf_expected;
218
}
219
#endif
220
passed = ( nret==expected_nret );
221
fprintf(( passed ? stdout : stderr ),
222
"T:%s|c89_snprintf(%lu,\"%s\", %d) nret=%d expected=%d\t[%s]\n", TESTNAME,
223
(unsigned long)count, fmt, (int)strlen(fmt), nret, expected_nret,
224
( passed ? "PASS" : "FAIL" ));
225
if( !passed ) ++nfailed;
226
227
if( count ){
228
passed = ( buf[expected_zero_at]=='\0' );
229
fprintf(( passed ? stdout : stderr ),
230
"T:%s|c89_snprintf(%lu,\"%s\", %d) s[%d]=%d expected=%d\t[%s]\n", TESTNAME,
231
(unsigned long)count, fmt, (int)strlen(fmt), expected_zero_at, buf[expected_zero_at],'\0',
232
( passed ? "PASS" : "FAIL" ));
233
if( !passed ) ++nfailed;
234
}
235
236
passed = ( strcmp(buf, expected)==0 );
237
fprintf((passed ? stdout : stderr),
238
"T:%s|c89_snprintf(%lu,\"%s\", %d)=\"%s\" expected=\"%s\"\t[%s]\n", TESTNAME,
239
(unsigned long)count, fmt, (int)strlen(fmt), buf, expected,
240
( passed ? "PASS" : "FAIL" ));
241
if( !passed ) ++nfailed;
242
}
243
244
if( nfailed ){
245
fprintf(stderr,"T:%s|FAILED %d tests\n\n", TESTNAME, nfailed);
246
}else{
247
printf("T:%s|PASSED\n\n", TESTNAME);
248
}
249
return nfailed;
250
}
251
252
int main(int argc, char *argv[])
253
{
254
int iarg;
255
struct testrun {
256
char rint, snprintf;
257
int status;
258
} testrun = {0, 0, 0};
259
260
printf("Usage: %s [TEST]...\n", argv[0]);
261
printf("Test MSVC C89 compatible implementations of selected C99 functions.\n"
262
"Run the selected TEST, by default 'all'; optionally exclude tests from the run.\n");
263
printf("Example: %s all -snprintf\n", argv[0]);
264
printf("\nTests:\n"
265
" all, rint, snprintf\n"
266
"\n -TEST\t\texclude the test from the run\n"
267
"\n"
268
);
269
if( argc>1
270
&& ( strcmp(argv[1], "/?")==0 || strcmp(argv[1], "/h")==0
271
|| strcmp(argv[1], "/H")==0 || strcmp(argv[1], "--help")==0 ) ){
272
return 0;
273
}
274
275
testrun.rint = testrun.snprintf = ( argc==1 );
276
for(iarg=1; iarg<argc; ++iarg){
277
const char *name = argv[iarg];
278
char runit = 1;
279
if( argv[iarg][0]=='-' ){
280
runit = 0;
281
name = &(argv[iarg][1]);
282
}
283
284
if( strcmp(name, "all")==0 ){
285
testrun.rint = testrun.snprintf = runit;
286
}else if( strcmp(name, "rint")==0 ){
287
testrun.rint = runit;
288
}else if( strcmp(name, "snprintf")==0 ){
289
testrun.snprintf = runit;
290
}else{
291
testrun.status = 2;
292
fprintf(stderr, "\nE|Invalid test requested: '%s'\n", name);
293
}
294
}
295
296
if( testrun.status==2 ){
297
return testrun.status;
298
}
299
300
#ifndef _MSC_VER
301
fprintf(stderr, "W|Non-MSVC mode: testing against the native implementations\n\n");
302
#endif
303
304
if( testrun.rint )
305
testrun.status |= test_rint();
306
307
if( testrun.snprintf )
308
testrun.status |= test_snprintf();
309
310
if( testrun.status==0 )
311
printf("\nI|All selected tests completed successfully\n");
312
else
313
fprintf(stderr, "\nE|Some of the selected tests failed\n");
314
return testrun.status;
315
}
316
317

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button