1 /* $OpenBSD: gcvt.c,v 1.9 2006/01/10 16:18:37 millert Exp $ */ 2 3 /* 4 * Copyright (c) 2002, 2003, 2006 Todd C. Miller <Todd.Miller@courtesan.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * Sponsored in part by the Defense Advanced Research Projects 19 * Agency (DARPA) and Air Force Research Laboratory, Air Force 20 * Materiel Command, USAF, under agreement number F39502-99-1-0512. 21 */ 22 23 #include <locale.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 extern char *__dtoa(double, int, int, int *, int *, char **); 29 30 char * 31 gcvt(double value, int ndigit, char *buf) 32 { 33 char *digits, *dst, *src; 34 int i, decpt, sign; 35 struct lconv *lconv; 36 37 lconv = localeconv(); 38 if (ndigit == 0) { 39 buf[0] = '\0'; 40 return (buf); 41 } 42 43 digits = __dtoa(value, 2, ndigit, &decpt, &sign, NULL); 44 if (decpt == 9999) { 45 /* 46 * Infinity or NaN, convert to inf or nan with sign. 47 * We assume the buffer is at least ndigit long. 48 */ 49 snprintf(buf, ndigit + 1, "%s%s", sign ? "-" : "", 50 *digits == 'I' ? "inf" : "nan"); 51 return (buf); 52 } 53 54 dst = buf; 55 if (sign) 56 *dst++ = '-'; 57 58 if (decpt < 0 || decpt > ndigit) { 59 /* exponential format (e.g. 1.2345e+13) */ 60 if (--decpt < 0) { 61 sign = 1; 62 decpt = -decpt; 63 } else 64 sign = 0; 65 src = digits; 66 *dst++ = *src++; 67 *dst++ = *lconv->decimal_point; 68 while (*src != '\0') 69 *dst++ = *src++; 70 *dst++ = 'e'; 71 if (sign) 72 *dst++ = '-'; 73 else 74 *dst++ = '+'; 75 if (decpt < 10) { 76 *dst++ = '0'; 77 *dst++ = '0' + decpt; 78 *dst = '\0'; 79 } else { 80 /* XXX - optimize */ 81 for (sign = decpt, i = 0; (sign /= 10) != 0; i++) 82 continue; 83 dst[i + 1] = '\0'; 84 while (decpt != 0) { 85 dst[i--] = '0' + decpt % 10; 86 decpt /= 10; 87 } 88 } 89 } else { 90 /* standard format */ 91 for (i = 0, src = digits; i < decpt; i++) { 92 if (*src != '\0') 93 *dst++ = *src++; 94 else 95 *dst++ = '0'; 96 } 97 if (*src != '\0') { 98 if (src == digits) 99 *dst++ = '0'; /* zero before decimal point */ 100 *dst++ = *lconv->decimal_point; 101 for (i = decpt; digits[i] != '\0'; i++) { 102 *dst++ = digits[i]; 103 } 104 } 105 *dst = '\0'; 106 } 107 return (buf); 108 } 109