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