1 /* Copyright (C) 2012 IBM 2 3 Author: Maynard Johnson <maynardj (at) us.ibm.com> 4 5 This program is free software; you can redistribute it and/or 6 modify it under the terms of the GNU General Public License as 7 published by the Free Software Foundation; either version 2 of the 8 License, or (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 18 02111-1307, USA. 19 20 The GNU General Public License is contained in the file COPYING. 21 */ 22 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <stdint.h> 26 27 #if defined(HAS_DFP) 28 29 typedef union stuff { 30 _Decimal64 dec_val; 31 _Decimal128 dec_val128; 32 unsigned long long u64_val; 33 struct { 34 #if defined(VGP_ppc64le_linux) 35 unsigned long long vall; 36 unsigned long long valu; 37 #else 38 unsigned long long valu; 39 unsigned long long vall; 40 #endif 41 } u128; 42 } dfp_val_t; 43 44 45 typedef unsigned char Bool; 46 #define True 1 47 #define False 0 48 49 50 #define ALLCR "cr0","cr1","cr2","cr3","cr4","cr5","cr6","cr7" 51 52 #define SET_CR(_arg) \ 53 __asm__ __volatile__ ("mtcr %0" : : "b"(_arg) : ALLCR ); 54 55 #define SET_XER(_arg) \ 56 __asm__ __volatile__ ("mtxer %0" : : "b"(_arg) : "xer" ); 57 58 #define GET_CR(_lval) \ 59 __asm__ __volatile__ ("mfcr %0" : "=b"(_lval) ) 60 61 #define GET_XER(_lval) \ 62 __asm__ __volatile__ ("mfxer %0" : "=b"(_lval) ) 63 64 #define GET_CR_XER(_lval_cr,_lval_xer) \ 65 do { GET_CR(_lval_cr); GET_XER(_lval_xer); } while (0) 66 67 #define SET_CR_ZERO \ 68 SET_CR(0) 69 70 #define SET_XER_ZERO \ 71 SET_XER(0) 72 73 #define SET_CR_XER_ZERO \ 74 do { SET_CR_ZERO; SET_XER_ZERO; } while (0) 75 76 #define SET_FPSCR_ZERO \ 77 do { double _d = 0.0; \ 78 __asm__ __volatile__ ("mtfsf 0xFF, %0" : : "f"(_d) ); \ 79 } while (0) 80 81 #define GET_FPSCR(_arg) \ 82 __asm__ __volatile__ ("mffs %0" : "=f"(_arg) ) 83 84 #define SET_FPSCR_DRN \ 85 __asm__ __volatile__ ("mtfsf 1, %0, 0, 1" : : "f"(f14) ) 86 87 88 // The assembly-level instructions being tested 89 90 /* In _test_dtstdc[q], DCM can be one of 6 possible data classes, numbered 0-5. 91 * In reality, DCM is a 6-bit mask field. We just test the individual values 92 * and assume that masking multiple values would work OK. 93 * BF is the condition register bit field which can range from 0-7. But for 94 * testing purposes, we only use BF values of '0' and '5'. 95 */ 96 static void _test_dtstdc(int BF, int DCM, dfp_val_t *val1, dfp_val_t *x1 __attribute__((unused))) 97 { 98 _Decimal64 f14 = val1->dec_val; 99 if (DCM < 0 || DCM > 5 || !(BF == 0 || BF == 5)) { 100 fprintf(stderr, "Invalid inputs to asm test: a=%d, b=%d\n", BF, DCM); 101 return; 102 } 103 switch (DCM) { 104 case 0: 105 if (BF) 106 __asm__ __volatile__ ("dtstdc 5, %0, 1" : : "f" (f14)); 107 else 108 __asm__ __volatile__ ("dtstdc 0, %0, 1" : : "f" (f14)); 109 break; 110 case 1: 111 if (BF) 112 __asm__ __volatile__ ("dtstdc 5, %0, 2" : : "f" (f14)); 113 else 114 __asm__ __volatile__ ("dtstdc 0, %0, 2" : : "f" (f14)); 115 break; 116 case 2: 117 if (BF) 118 __asm__ __volatile__ ("dtstdc 5, %0, 4" : : "f" (f14)); 119 else 120 __asm__ __volatile__ ("dtstdc 0, %0, 4" : : "f" (f14)); 121 break; 122 case 3: 123 if (BF) 124 __asm__ __volatile__ ("dtstdc 5, %0, 8" : : "f" (f14)); 125 else 126 __asm__ __volatile__ ("dtstdc 0, %0, 8" : : "f" (f14)); 127 break; 128 case 4: 129 if (BF) 130 __asm__ __volatile__ ("dtstdc 5, %0, 16" : : "f" (f14)); 131 else 132 __asm__ __volatile__ ("dtstdc 0, %0, 16" : : "f" (f14)); 133 break; 134 case 5: 135 if (BF) 136 __asm__ __volatile__ ("dtstdc 5, %0, 32" : : "f" (f14)); 137 else 138 __asm__ __volatile__ ("dtstdc 0, %0, 32" : : "f" (f14)); 139 break; 140 default: 141 break; 142 } 143 } 144 145 static void _test_dtstdcq(int BF, int DCM, dfp_val_t *val1, dfp_val_t *x1 __attribute__((unused))) 146 { 147 _Decimal128 f14 = val1->dec_val128; 148 if (DCM < 0 || DCM > 5 || !(BF == 0 || BF == 5)) { 149 fprintf(stderr, "Invalid inputs to asm test: a=%d, b=%d\n", BF, DCM); 150 return; 151 } 152 switch (DCM) { 153 case 0: 154 if (BF) 155 __asm__ __volatile__ ("dtstdcq 5, %0, 1" : : "f" (f14)); 156 else 157 __asm__ __volatile__ ("dtstdcq 0, %0, 1" : : "f" (f14)); 158 break; 159 case 1: 160 if (BF) 161 __asm__ __volatile__ ("dtstdcq 5, %0, 2" : : "f" (f14)); 162 else 163 __asm__ __volatile__ ("dtstdcq 0, %0, 2" : : "f" (f14)); 164 break; 165 case 2: 166 if (BF) 167 __asm__ __volatile__ ("dtstdcq 5, %0, 4" : : "f" (f14)); 168 else 169 __asm__ __volatile__ ("dtstdcq 0, %0, 4" : : "f" (f14)); 170 break; 171 case 3: 172 if (BF) 173 __asm__ __volatile__ ("dtstdcq 5, %0, 8" : : "f" (f14)); 174 else 175 __asm__ __volatile__ ("dtstdcq 0, %0, 8" : : "f" (f14)); 176 break; 177 case 4: 178 if (BF) 179 __asm__ __volatile__ ("dtstdcq 5, %0, 16" : : "f" (f14)); 180 else 181 __asm__ __volatile__ ("dtstdcq 0, %0, 16" : : "f" (f14)); 182 break; 183 case 5: 184 if (BF) 185 __asm__ __volatile__ ("dtstdcq 5, %0, 32" : : "f" (f14)); 186 else 187 __asm__ __volatile__ ("dtstdcq 0, %0, 32" : : "f" (f14)); 188 break; 189 default: 190 break; 191 } 192 } 193 194 /* In _test_dtstdg[q], DGM can be one of 6 possible data groups, numbered 0-5. 195 * In reality, DGM is a 6-bit mask field. We just test the individual values 196 * and assume that masking multiple values would work OK. 197 * BF is the condition register bit field which can range from 0-7. But for 198 * testing purposes, we only use BF values of '0' and '5'. 199 */ 200 static void _test_dtstdg(int BF, int DGM, dfp_val_t *val1, dfp_val_t *x1 __attribute__((unused))) 201 { 202 _Decimal64 f14 = val1->dec_val; 203 if (DGM < 0 || DGM > 5 || !(BF == 0 || BF == 5)) { 204 fprintf(stderr, "Invalid inputs to asm test: a=%d, b=%d\n", BF, DGM); 205 return; 206 } 207 switch (DGM) { 208 case 0: 209 if (BF) 210 __asm__ __volatile__ ("dtstdg 5, %0, 1" : : "f" (f14)); 211 else 212 __asm__ __volatile__ ("dtstdg 0, %0, 1" : : "f" (f14)); 213 break; 214 case 1: 215 if (BF) 216 __asm__ __volatile__ ("dtstdg 5, %0, 2" : : "f" (f14)); 217 else 218 __asm__ __volatile__ ("dtstdg 0, %0, 2" : : "f" (f14)); 219 break; 220 case 2: 221 if (BF) 222 __asm__ __volatile__ ("dtstdg 5, %0, 4" : : "f" (f14)); 223 else 224 __asm__ __volatile__ ("dtstdg 0, %0, 4" : : "f" (f14)); 225 break; 226 case 3: 227 if (BF) 228 __asm__ __volatile__ ("dtstdg 5, %0, 8" : : "f" (f14)); 229 else 230 __asm__ __volatile__ ("dtstdg 0, %0, 8" : : "f" (f14)); 231 break; 232 case 4: 233 if (BF) 234 __asm__ __volatile__ ("dtstdg 5, %0, 16" : : "f" (f14)); 235 else 236 __asm__ __volatile__ ("dtstdg 0, %0, 16" : : "f" (f14)); 237 break; 238 case 5: 239 if (BF) 240 __asm__ __volatile__ ("dtstdg 5, %0, 32" : : "f" (f14)); 241 else 242 __asm__ __volatile__ ("dtstdg 0, %0, 32" : : "f" (f14)); 243 break; 244 default: 245 break; 246 } 247 } 248 249 static void _test_dtstdgq(int BF, int DGM, dfp_val_t *val1, dfp_val_t *x1 __attribute__((unused))) 250 { 251 _Decimal128 f14 = val1->dec_val128; 252 if (DGM < 0 || DGM > 5 || !(BF == 0 || BF == 5)) { 253 fprintf(stderr, "Invalid inputs to asm test: a=%d, b=%d\n", BF, DGM); 254 return; 255 } 256 switch (DGM) { 257 case 0: 258 if (BF) 259 __asm__ __volatile__ ("dtstdgq 5, %0, 1" : : "f" (f14)); 260 else 261 __asm__ __volatile__ ("dtstdgq 0, %0, 1" : : "f" (f14)); 262 break; 263 case 1: 264 if (BF) 265 __asm__ __volatile__ ("dtstdgq 5, %0, 2" : : "f" (f14)); 266 else 267 __asm__ __volatile__ ("dtstdgq 0, %0, 2" : : "f" (f14)); 268 break; 269 case 2: 270 if (BF) 271 __asm__ __volatile__ ("dtstdgq 5, %0, 4" : : "f" (f14)); 272 else 273 __asm__ __volatile__ ("dtstdgq 0, %0, 4" : : "f" (f14)); 274 break; 275 case 3: 276 if (BF) 277 __asm__ __volatile__ ("dtstdgq 5, %0, 8" : : "f" (f14)); 278 else 279 __asm__ __volatile__ ("dtstdgq 0, %0, 8" : : "f" (f14)); 280 break; 281 case 4: 282 if (BF) 283 __asm__ __volatile__ ("dtstdgq 5, %0, 16" : : "f" (f14)); 284 else 285 __asm__ __volatile__ ("dtstdgq 0, %0, 16" : : "f" (f14)); 286 break; 287 case 5: 288 if (BF) 289 __asm__ __volatile__ ("dtstdgq 5, %0, 32" : : "f" (f14)); 290 else 291 __asm__ __volatile__ ("dtstdgq 0, %0, 32" : : "f" (f14)); 292 break; 293 default: 294 break; 295 } 296 } 297 298 /* In _test_dtstex[q], BF is the condition register bit field indicating the 299 * CR field in which the result of the test should be placed. BF can range 300 * from 0-7, but for testing purposes, we only use BF values of '4' and '7'. 301 */ 302 static void 303 _test_dtstex(int BF, int x __attribute__((unused)), dfp_val_t *val1, dfp_val_t *val2) 304 { 305 _Decimal64 f14 = val1->dec_val; 306 _Decimal64 f16 = val2->dec_val; 307 if (!(BF == 4 || BF == 7)) { 308 fprintf(stderr, "Invalid input to asm test: a=%d\n", BF); 309 return; 310 } 311 switch (BF) { 312 case 4: 313 __asm__ __volatile__ ("dtstex 4, %0, %1" : : "f" (f14),"f" (f16)); 314 break; 315 case 7: 316 __asm__ __volatile__ ("dtstex 7, %0, %1" : : "f" (f14),"f" (f16)); 317 break; 318 default: 319 break; 320 } 321 } 322 323 static void _test_dtstexq(int BF, int x __attribute__((unused)), dfp_val_t *val1, dfp_val_t *val2) 324 { 325 _Decimal128 f14 = val1->dec_val128; 326 _Decimal128 f16 = val2->dec_val128; 327 if (!(BF == 4 || BF == 7)) { 328 fprintf(stderr, "Invalid input to asm test: a=%d\n", BF); 329 return; 330 } 331 switch (BF) { 332 case 4: 333 __asm__ __volatile__ ("dtstexq 4, %0, %1" : : "f" (f14),"f" (f16)); 334 break; 335 case 7: 336 __asm__ __volatile__ ("dtstexq 7, %0, %1" : : "f" (f14),"f" (f16)); 337 break; 338 default: 339 break; 340 } 341 } 342 343 344 345 typedef void (*test_funcp_t)(int a, int b, dfp_val_t *val1, dfp_val_t *val2); 346 typedef void (*test_driver_func_t)(void); 347 typedef struct test_table 348 { 349 test_driver_func_t test_category; 350 char * name; 351 } test_table_t; 352 353 /* 354 * 345.0DD (0x2207c00000000000 0xe50) 355 * 1.2300e+5DD (0x2207c00000000000 0x14c000) 356 * -16.0DD (0xa207c00000000000 0xe0) 357 * 0.00189DD (0x2206c00000000000 0xcf) 358 * -4.1235DD (0xa205c00000000000 0x10a395bcf) 359 * 9.8399e+20DD (0x2209400000000000 0x253f1f534acdd4) 360 * 0DD (0x2208000000000000 0x0) 361 * 0DD (0x2208000000000000 0x0) 362 * infDD (0x7800000000000000 0x0) 363 * nanDD (0x7c00000000000000 0x0 364 */ 365 static unsigned long long dfp128_vals[] = { 366 // Some finite numbers 367 0x2207c00000000000ULL, 0x0000000000000e50ULL, 368 0x2207c00000000000ULL, 0x000000000014c000ULL, 369 0xa207c00000000000ULL, 0x00000000000000e0ULL, 370 0x2206c00000000000ULL, 0x00000000000000cfULL, 371 0xa205c00000000000ULL, 0x000000010a395bcfULL, 372 0x6209400000fd0000ULL, 0x00253f1f534acdd4ULL, // huge number 373 0x000400000089b000ULL, 0x0a6000d000000049ULL, // very small number 374 // flavors of zero 375 0x2208000000000000ULL, 0x0000000000000000ULL, 376 0xa208000000000000ULL, 0x0000000000000000ULL, // negative 377 0xa248000000000000ULL, 0x0000000000000000ULL, 378 // flavors of NAN 379 0x7c00000000000000ULL, 0x0000000000000000ULL, // quiet 380 0xfc00000000000000ULL, 0xc00100035b007700ULL, 381 0x7e00000000000000ULL, 0xfe000000d0e0a0d0ULL, // signaling 382 // flavors of Infinity 383 0x7800000000000000ULL, 0x0000000000000000ULL, 384 0xf800000000000000ULL, 0x0000000000000000ULL, // negative 385 0xf900000000000000ULL, 0x0000000000000000ULL 386 }; 387 388 static unsigned long long dfp64_vals[] = { 389 // various finite numbers 390 0x2234000000000e50ULL, 391 0x223400000014c000ULL, 392 0xa2340000000000e0ULL,// negative 393 0x22240000000000cfULL, 394 0xa21400010a395bcfULL,// negative 395 0x6e4d3f1f534acdd4ULL,// huge number 396 0x000400000089b000ULL,// very small number 397 // flavors of zero 398 0x2238000000000000ULL, 399 0xa238000000000000ULL, 400 0x4248000000000000ULL, 401 // flavors of NAN 402 0x7e34000000000111ULL, 403 0xfe000000d0e0a0d0ULL,//signaling 404 0xfc00000000000000ULL,//quiet 405 // flavors of Infinity 406 0x7800000000000000ULL, 407 0xf800000000000000ULL,//negative 408 0x7a34000000000000ULL, 409 }; 410 411 // Both Long and Quad arrays of DFP values should have the same length, so it 412 // doesn't matter which array I use for calculating the following #define. 413 #define NUM_DFP_VALS (sizeof(dfp64_vals)/8) 414 415 typedef struct dfp_test_args { 416 int fra_idx; 417 int frb_idx; 418 } dfp_test_args_t; 419 420 421 // Index pairs from dfp64_vals array to be used with dfp_two_arg_tests 422 static dfp_test_args_t dfp_2args_x1[] = { 423 {0, 1}, 424 {2, 1}, 425 {4, 3}, 426 {6, 0}, 427 {2, 4}, 428 {5, 1}, 429 {5, 2}, 430 {7, 1}, 431 {7, 2}, 432 {8, 0}, 433 {8, 1}, 434 {8, 2}, 435 {7, 8}, 436 {12, 14}, 437 {12, 1}, 438 {12, 13}, 439 {12, 12}, 440 {12, 11}, 441 {11, 14}, 442 {11, 0}, 443 {11, 13}, 444 {11, 11}, 445 {14, 14}, 446 {14, 3}, 447 {14, 15}, 448 }; 449 450 typedef enum { 451 LONG_TEST, 452 QUAD_TEST 453 } precision_type_t; 454 455 typedef struct dfp_test 456 { 457 test_funcp_t test_func; 458 const char * name; 459 dfp_test_args_t * targs; 460 int num_tests; 461 precision_type_t precision; 462 const char * op; 463 } dfp_test_t; 464 465 typedef struct dfp_one_arg_test 466 { 467 test_funcp_t test_func; 468 const char * name; 469 precision_type_t precision; 470 const char * op; 471 } dfp_one_arg_test_t; 472 473 474 475 static dfp_one_arg_test_t 476 dfp_ClassAndGroupTest_tests[] = { 477 { &_test_dtstdc, "dtstdc", LONG_TEST, "[tCls]"}, 478 { &_test_dtstdcq, "dtstdcq", QUAD_TEST, "[tCls]"}, 479 { &_test_dtstdg, "dtstdg", LONG_TEST, "[tGrp]"}, 480 { &_test_dtstdgq, "dtstdgq", QUAD_TEST, "[tGrp]"}, 481 { NULL, NULL, 0, NULL} 482 }; 483 484 static void test_dfp_ClassAndGroupTest_ops(void) 485 { 486 test_funcp_t func; 487 dfp_val_t test_val, dummy; 488 489 int k = 0; 490 491 while ((func = dfp_ClassAndGroupTest_tests[k].test_func)) { 492 int i; 493 dfp_one_arg_test_t test_def = dfp_ClassAndGroupTest_tests[k]; 494 495 for (i = 0; i < NUM_DFP_VALS; i++) { 496 int data_class_OR_group, BF = 0; 497 Bool repeat = True; 498 499 if (test_def.precision == LONG_TEST) { 500 test_val.u64_val = dfp64_vals[i]; 501 } else { 502 test_val.u128.valu = dfp128_vals[i * 2]; 503 test_val.u128.vall = dfp128_vals[(i * 2) + 1]; 504 } 505 506 again: 507 for (data_class_OR_group = 0; data_class_OR_group < 6; data_class_OR_group++) { 508 unsigned int condreg; 509 unsigned int flags; 510 SET_FPSCR_ZERO; 511 SET_CR_XER_ZERO; 512 513 /* There is an ABI change in how 128 bit arguments are aligned 514 * with GCC 5.0. The compiler generates a "note" about this 515 * starting with GCC 4.8. To avoid generating the "note", pass 516 * the address of the 128-bit arguments rather then the value. 517 */ 518 (*func)(BF, data_class_OR_group, &test_val, &dummy); 519 GET_CR(flags); 520 521 condreg = ((flags >> (4 * (7-BF)))) & 0xf; 522 printf("%s (DC/DG=%d) %s", test_def.name, data_class_OR_group, 523 test_def.op); 524 if (test_def.precision == QUAD_TEST) { 525 printf("%016llx %016llx", test_val.u128.valu, test_val.u128.vall); 526 } else { 527 printf("%016llx", test_val.u64_val); 528 } 529 530 //%016llx 531 printf(" => %x (BF=%d)\n", condreg, BF); 532 } 533 if (repeat) { 534 repeat = False; 535 BF = 5; 536 goto again; 537 } 538 } 539 k++; 540 printf( "\n" ); 541 } 542 } 543 544 545 static dfp_test_t 546 dfp_ExpTest_tests[] = { 547 { &_test_dtstex, "dtstex", dfp_2args_x1, 25, LONG_TEST, "[tExp]"}, 548 { &_test_dtstexq, "dtstexq", dfp_2args_x1, 25, QUAD_TEST, "[tExp]"}, 549 { NULL, NULL, NULL, 0, 0, NULL} 550 }; 551 552 553 static void test_dfp_ExpTest_ops(void) 554 { 555 dfp_val_t test_val1, test_val2; 556 test_funcp_t func; 557 int k = 0; 558 559 while ((func = dfp_ExpTest_tests[k].test_func)) { 560 /* BF is a 3-bit instruction field that indicates the CR field in which the 561 * result of the test should be placed. We won't iterate through all 562 * 8 possible BF values since storing compare results to a given field is 563 * a well-tested mechanism in VEX. But we will test two BF values, just as 564 * a sniff-test. 565 */ 566 int i, repeat = 1, BF = 4; 567 dfp_test_t test_def = dfp_ExpTest_tests[k]; 568 569 again: 570 for (i = 0; i < test_def.num_tests; i++) { 571 unsigned int condreg; 572 unsigned int flags; 573 574 if (test_def.precision == LONG_TEST) { 575 test_val1.u64_val = dfp64_vals[test_def.targs[i].fra_idx]; 576 test_val2.u64_val = dfp64_vals[test_def.targs[i].frb_idx]; 577 } else { 578 test_val1.u128.valu = dfp128_vals[test_def.targs[i].fra_idx * 2]; 579 test_val1.u128.vall = dfp128_vals[(test_def.targs[i].fra_idx * 2) + 1]; 580 test_val2.u128.valu = dfp128_vals[test_def.targs[i].frb_idx * 2]; 581 test_val2.u128.vall = dfp128_vals[(test_def.targs[i].frb_idx * 2) + 1]; 582 } 583 584 SET_FPSCR_ZERO; 585 SET_CR_XER_ZERO; 586 /* There is an ABI change in how 128 bit arguments are aligned 587 * with GCC 5.0. The compiler generates a "note" about this 588 * starting with GCC 4.8. To avoid generating the "note", pass 589 * the address of the 128-bit arguments rather then the value. 590 */ 591 (*func)(BF, 0, &test_val1, &test_val2); 592 GET_CR(flags); 593 594 condreg = ((flags >> (4 * (7-BF)))) & 0xf; 595 printf("%s ", test_def.name); 596 if (test_def.precision == LONG_TEST) { 597 printf("%016llx %s %016llx ", 598 test_val1.u64_val, test_def.op, test_val2.u64_val); 599 } else { 600 printf("%016llx %016llx %s %016llx %016llx ", 601 test_val1.u128.valu, test_val1.u128.vall, test_def.op, test_val2.u128.valu, test_val2.u128.vall); 602 } 603 printf(" => %x (BF=%d)\n", condreg, BF); 604 } 605 if (repeat) { 606 repeat = 0; 607 BF = 7; 608 goto again; 609 } 610 k++; 611 printf( "\n" ); 612 } 613 } 614 615 616 static test_table_t 617 all_tests[] = 618 { 619 { &test_dfp_ExpTest_ops, 620 "Test DFP exponent test instructions"}, 621 { &test_dfp_ClassAndGroupTest_ops, 622 "Test DFP class and group test instructions"}, 623 { NULL, NULL } 624 }; 625 #endif // HAS_DFP 626 627 int main() { 628 #if defined(HAS_DFP) 629 630 test_table_t aTest; 631 test_driver_func_t func; 632 int i = 0; 633 634 while ((func = all_tests[i].test_category)) { 635 aTest = all_tests[i]; 636 printf( "%s\n", aTest.name ); 637 (*func)(); 638 i++; 639 } 640 641 #endif // HAS_DFP 642 return 0; 643 } 644