4 ******************************************************************************
5 This file contains routines that can be bound to a Postgres backend and
6 called by the backend in the process of processing queries. The calling
7 format for these routines is dictated by Postgres architecture.
8 ******************************************************************************/
15 #include "access/gist.h"
16 #include "access/stratnum.h"
22 #define DatumGetSegP(X) ((SEG *) DatumGetPointer(X))
23 #define PG_GETARG_SEG_P(n) DatumGetSegP(PG_GETARG_DATUM(n))
28 #define GIST_QUERY_DEBUG
34 * Auxiliary data structure for picksplit method.
41 } gseg_picksplit_item
;
44 ** Input/Output routines
46 PG_FUNCTION_INFO_V1(seg_in
);
47 PG_FUNCTION_INFO_V1(seg_out
);
48 PG_FUNCTION_INFO_V1(seg_size
);
49 PG_FUNCTION_INFO_V1(seg_lower
);
50 PG_FUNCTION_INFO_V1(seg_upper
);
51 PG_FUNCTION_INFO_V1(seg_center
);
54 ** GiST support methods
56 PG_FUNCTION_INFO_V1(gseg_consistent
);
57 PG_FUNCTION_INFO_V1(gseg_compress
);
58 PG_FUNCTION_INFO_V1(gseg_decompress
);
59 PG_FUNCTION_INFO_V1(gseg_picksplit
);
60 PG_FUNCTION_INFO_V1(gseg_penalty
);
61 PG_FUNCTION_INFO_V1(gseg_union
);
62 PG_FUNCTION_INFO_V1(gseg_same
);
63 static Datum
gseg_leaf_consistent(Datum key
, Datum query
, StrategyNumber strategy
);
64 static Datum
gseg_internal_consistent(Datum key
, Datum query
, StrategyNumber strategy
);
65 static Datum
gseg_binary_union(Datum r1
, Datum r2
, int *sizep
);
69 ** R-tree support functions
71 PG_FUNCTION_INFO_V1(seg_same
);
72 PG_FUNCTION_INFO_V1(seg_contains
);
73 PG_FUNCTION_INFO_V1(seg_contained
);
74 PG_FUNCTION_INFO_V1(seg_overlap
);
75 PG_FUNCTION_INFO_V1(seg_left
);
76 PG_FUNCTION_INFO_V1(seg_over_left
);
77 PG_FUNCTION_INFO_V1(seg_right
);
78 PG_FUNCTION_INFO_V1(seg_over_right
);
79 PG_FUNCTION_INFO_V1(seg_union
);
80 PG_FUNCTION_INFO_V1(seg_inter
);
81 static void rt_seg_size(SEG
*a
, float *size
);
86 PG_FUNCTION_INFO_V1(seg_cmp
);
87 PG_FUNCTION_INFO_V1(seg_lt
);
88 PG_FUNCTION_INFO_V1(seg_le
);
89 PG_FUNCTION_INFO_V1(seg_gt
);
90 PG_FUNCTION_INFO_V1(seg_ge
);
91 PG_FUNCTION_INFO_V1(seg_different
);
94 ** Auxiliary functions
96 static int restore(char *result
, float val
, int n
);
99 /*****************************************************************************
100 * Input/Output functions
101 *****************************************************************************/
104 seg_in(PG_FUNCTION_ARGS
)
106 char *str
= PG_GETARG_CSTRING(0);
107 SEG
*result
= palloc(sizeof(SEG
));
109 seg_scanner_init(str
);
111 if (seg_yyparse(result
, fcinfo
->context
) != 0)
112 seg_yyerror(result
, fcinfo
->context
, "bogus input");
114 seg_scanner_finish();
116 PG_RETURN_POINTER(result
);
120 seg_out(PG_FUNCTION_ARGS
)
122 SEG
*seg
= PG_GETARG_SEG_P(0);
126 p
= result
= (char *) palloc(40);
128 if (seg
->l_ext
== '>' || seg
->l_ext
== '<' || seg
->l_ext
== '~')
129 p
+= sprintf(p
, "%c", seg
->l_ext
);
131 if (seg
->lower
== seg
->upper
&& seg
->l_ext
== seg
->u_ext
)
134 * indicates that this interval was built by seg_in off a single point
136 p
+= restore(p
, seg
->lower
, seg
->l_sigd
);
140 if (seg
->l_ext
!= '-')
142 /* print the lower boundary if exists */
143 p
+= restore(p
, seg
->lower
, seg
->l_sigd
);
144 p
+= sprintf(p
, " ");
146 p
+= sprintf(p
, "..");
147 if (seg
->u_ext
!= '-')
149 /* print the upper boundary if exists */
150 p
+= sprintf(p
, " ");
151 if (seg
->u_ext
== '>' || seg
->u_ext
== '<' || seg
->l_ext
== '~')
152 p
+= sprintf(p
, "%c", seg
->u_ext
);
153 p
+= restore(p
, seg
->upper
, seg
->u_sigd
);
157 PG_RETURN_CSTRING(result
);
161 seg_center(PG_FUNCTION_ARGS
)
163 SEG
*seg
= PG_GETARG_SEG_P(0);
165 PG_RETURN_FLOAT4(((float) seg
->lower
+ (float) seg
->upper
) / 2.0);
169 seg_lower(PG_FUNCTION_ARGS
)
171 SEG
*seg
= PG_GETARG_SEG_P(0);
173 PG_RETURN_FLOAT4(seg
->lower
);
177 seg_upper(PG_FUNCTION_ARGS
)
179 SEG
*seg
= PG_GETARG_SEG_P(0);
181 PG_RETURN_FLOAT4(seg
->upper
);
185 /*****************************************************************************
187 *****************************************************************************/
190 ** The GiST Consistent method for segments
191 ** Should return false if for all data items x below entry,
192 ** the predicate x op query == false, where op is the oper
193 ** corresponding to strategy in the pg_amop table.
196 gseg_consistent(PG_FUNCTION_ARGS
)
198 GISTENTRY
*entry
= (GISTENTRY
*) PG_GETARG_POINTER(0);
199 Datum query
= PG_GETARG_DATUM(1);
200 StrategyNumber strategy
= (StrategyNumber
) PG_GETARG_UINT16(2);
202 /* Oid subtype = PG_GETARG_OID(3); */
203 bool *recheck
= (bool *) PG_GETARG_POINTER(4);
205 /* All cases served by this function are exact */
209 * if entry is not leaf, use gseg_internal_consistent, else use
210 * gseg_leaf_consistent
212 if (GIST_LEAF(entry
))
213 return gseg_leaf_consistent(entry
->key
, query
, strategy
);
215 return gseg_internal_consistent(entry
->key
, query
, strategy
);
219 ** The GiST Union method for segments
220 ** returns the minimal bounding seg that encloses all the entries in entryvec
223 gseg_union(PG_FUNCTION_ARGS
)
225 GistEntryVector
*entryvec
= (GistEntryVector
*) PG_GETARG_POINTER(0);
226 int *sizep
= (int *) PG_GETARG_POINTER(1);
233 fprintf(stderr
, "union\n");
236 numranges
= entryvec
->n
;
237 tmp
= entryvec
->vector
[0].key
;
238 *sizep
= sizeof(SEG
);
240 for (i
= 1; i
< numranges
; i
++)
242 out
= gseg_binary_union(tmp
, entryvec
->vector
[i
].key
, sizep
);
246 PG_RETURN_DATUM(out
);
250 ** GiST Compress and Decompress methods for segments
251 ** do not do anything.
254 gseg_compress(PG_FUNCTION_ARGS
)
256 PG_RETURN_POINTER(PG_GETARG_POINTER(0));
260 gseg_decompress(PG_FUNCTION_ARGS
)
262 PG_RETURN_POINTER(PG_GETARG_POINTER(0));
266 ** The GiST Penalty method for segments
267 ** As in the R-tree paper, we use change in area as our penalty metric
270 gseg_penalty(PG_FUNCTION_ARGS
)
272 GISTENTRY
*origentry
= (GISTENTRY
*) PG_GETARG_POINTER(0);
273 GISTENTRY
*newentry
= (GISTENTRY
*) PG_GETARG_POINTER(1);
274 float *result
= (float *) PG_GETARG_POINTER(2);
279 ud
= DatumGetSegP(DirectFunctionCall2(seg_union
,
282 rt_seg_size(ud
, &tmp1
);
283 rt_seg_size(DatumGetSegP(origentry
->key
), &tmp2
);
284 *result
= tmp1
- tmp2
;
287 fprintf(stderr
, "penalty\n");
288 fprintf(stderr
, "\t%g\n", *result
);
291 PG_RETURN_POINTER(result
);
295 * Compare function for gseg_picksplit_item: sort by center.
298 gseg_picksplit_item_cmp(const void *a
, const void *b
)
300 const gseg_picksplit_item
*i1
= (const gseg_picksplit_item
*) a
;
301 const gseg_picksplit_item
*i2
= (const gseg_picksplit_item
*) b
;
303 if (i1
->center
< i2
->center
)
305 else if (i1
->center
== i2
->center
)
312 * The GiST PickSplit method for segments
314 * We used to use Guttman's split algorithm here, but since the data is 1-D
315 * it's easier and more robust to just sort the segments by center-point and
316 * split at the middle.
319 gseg_picksplit(PG_FUNCTION_ARGS
)
321 GistEntryVector
*entryvec
= (GistEntryVector
*) PG_GETARG_POINTER(0);
322 GIST_SPLITVEC
*v
= (GIST_SPLITVEC
*) PG_GETARG_POINTER(1);
327 gseg_picksplit_item
*sort_items
;
331 OffsetNumber firstright
;
334 fprintf(stderr
, "picksplit\n");
337 /* Valid items in entryvec->vector[] are indexed 1..maxoff */
338 maxoff
= entryvec
->n
- 1;
341 * Prepare the auxiliary array and sort it.
343 sort_items
= (gseg_picksplit_item
*)
344 palloc(maxoff
* sizeof(gseg_picksplit_item
));
345 for (i
= 1; i
<= maxoff
; i
++)
347 seg
= DatumGetSegP(entryvec
->vector
[i
].key
);
348 /* center calculation is done this way to avoid possible overflow */
349 sort_items
[i
- 1].center
= seg
->lower
* 0.5f
+ seg
->upper
* 0.5f
;
350 sort_items
[i
- 1].index
= i
;
351 sort_items
[i
- 1].data
= seg
;
353 qsort(sort_items
, maxoff
, sizeof(gseg_picksplit_item
),
354 gseg_picksplit_item_cmp
);
356 /* sort items below "firstright" will go into the left side */
357 firstright
= maxoff
/ 2;
359 v
->spl_left
= (OffsetNumber
*) palloc(maxoff
* sizeof(OffsetNumber
));
360 v
->spl_right
= (OffsetNumber
*) palloc(maxoff
* sizeof(OffsetNumber
));
363 right
= v
->spl_right
;
367 * Emit segments to the left output page, and compute its bounding box.
369 seg_l
= (SEG
*) palloc(sizeof(SEG
));
370 memcpy(seg_l
, sort_items
[0].data
, sizeof(SEG
));
371 *left
++ = sort_items
[0].index
;
373 for (i
= 1; i
< firstright
; i
++)
375 Datum sortitem
= PointerGetDatum(sort_items
[i
].data
);
377 seg_l
= DatumGetSegP(DirectFunctionCall2(seg_union
,
378 PointerGetDatum(seg_l
),
380 *left
++ = sort_items
[i
].index
;
385 * Likewise for the right page.
387 seg_r
= (SEG
*) palloc(sizeof(SEG
));
388 memcpy(seg_r
, sort_items
[firstright
].data
, sizeof(SEG
));
389 *right
++ = sort_items
[firstright
].index
;
391 for (i
= firstright
+ 1; i
< maxoff
; i
++)
393 Datum sortitem
= PointerGetDatum(sort_items
[i
].data
);
395 seg_r
= DatumGetSegP(DirectFunctionCall2(seg_union
,
396 PointerGetDatum(seg_r
),
398 *right
++ = sort_items
[i
].index
;
402 v
->spl_ldatum
= PointerGetDatum(seg_l
);
403 v
->spl_rdatum
= PointerGetDatum(seg_r
);
405 PG_RETURN_POINTER(v
);
412 gseg_same(PG_FUNCTION_ARGS
)
414 bool *result
= (bool *) PG_GETARG_POINTER(2);
416 if (DirectFunctionCall2(seg_same
, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1)))
422 fprintf(stderr
, "same: %s\n", (*result
? "TRUE" : "FALSE"));
425 PG_RETURN_POINTER(result
);
432 gseg_leaf_consistent(Datum key
, Datum query
, StrategyNumber strategy
)
436 #ifdef GIST_QUERY_DEBUG
437 fprintf(stderr
, "leaf_consistent, %d\n", strategy
);
442 case RTLeftStrategyNumber
:
443 retval
= DirectFunctionCall2(seg_left
, key
, query
);
445 case RTOverLeftStrategyNumber
:
446 retval
= DirectFunctionCall2(seg_over_left
, key
, query
);
448 case RTOverlapStrategyNumber
:
449 retval
= DirectFunctionCall2(seg_overlap
, key
, query
);
451 case RTOverRightStrategyNumber
:
452 retval
= DirectFunctionCall2(seg_over_right
, key
, query
);
454 case RTRightStrategyNumber
:
455 retval
= DirectFunctionCall2(seg_right
, key
, query
);
457 case RTSameStrategyNumber
:
458 retval
= DirectFunctionCall2(seg_same
, key
, query
);
460 case RTContainsStrategyNumber
:
461 case RTOldContainsStrategyNumber
:
462 retval
= DirectFunctionCall2(seg_contains
, key
, query
);
464 case RTContainedByStrategyNumber
:
465 case RTOldContainedByStrategyNumber
:
466 retval
= DirectFunctionCall2(seg_contained
, key
, query
);
472 PG_RETURN_DATUM(retval
);
476 gseg_internal_consistent(Datum key
, Datum query
, StrategyNumber strategy
)
480 #ifdef GIST_QUERY_DEBUG
481 fprintf(stderr
, "internal_consistent, %d\n", strategy
);
486 case RTLeftStrategyNumber
:
488 !DatumGetBool(DirectFunctionCall2(seg_over_right
, key
, query
));
490 case RTOverLeftStrategyNumber
:
492 !DatumGetBool(DirectFunctionCall2(seg_right
, key
, query
));
494 case RTOverlapStrategyNumber
:
496 DatumGetBool(DirectFunctionCall2(seg_overlap
, key
, query
));
498 case RTOverRightStrategyNumber
:
500 !DatumGetBool(DirectFunctionCall2(seg_left
, key
, query
));
502 case RTRightStrategyNumber
:
504 !DatumGetBool(DirectFunctionCall2(seg_over_left
, key
, query
));
506 case RTSameStrategyNumber
:
507 case RTContainsStrategyNumber
:
508 case RTOldContainsStrategyNumber
:
510 DatumGetBool(DirectFunctionCall2(seg_contains
, key
, query
));
512 case RTContainedByStrategyNumber
:
513 case RTOldContainedByStrategyNumber
:
515 DatumGetBool(DirectFunctionCall2(seg_overlap
, key
, query
));
521 PG_RETURN_BOOL(retval
);
525 gseg_binary_union(Datum r1
, Datum r2
, int *sizep
)
529 retval
= DirectFunctionCall2(seg_union
, r1
, r2
);
530 *sizep
= sizeof(SEG
);
537 seg_contains(PG_FUNCTION_ARGS
)
539 SEG
*a
= PG_GETARG_SEG_P(0);
540 SEG
*b
= PG_GETARG_SEG_P(1);
542 PG_RETURN_BOOL((a
->lower
<= b
->lower
) && (a
->upper
>= b
->upper
));
546 seg_contained(PG_FUNCTION_ARGS
)
548 Datum a
= PG_GETARG_DATUM(0);
549 Datum b
= PG_GETARG_DATUM(1);
551 PG_RETURN_DATUM(DirectFunctionCall2(seg_contains
, b
, a
));
554 /*****************************************************************************
555 * Operator class for R-tree indexing
556 *****************************************************************************/
559 seg_same(PG_FUNCTION_ARGS
)
561 int cmp
= DatumGetInt32(DirectFunctionCall2(seg_cmp
,
563 PG_GETARG_DATUM(1)));
565 PG_RETURN_BOOL(cmp
== 0);
568 /* seg_overlap -- does a overlap b?
571 seg_overlap(PG_FUNCTION_ARGS
)
573 SEG
*a
= PG_GETARG_SEG_P(0);
574 SEG
*b
= PG_GETARG_SEG_P(1);
576 PG_RETURN_BOOL(((a
->upper
>= b
->upper
) && (a
->lower
<= b
->upper
)) ||
577 ((b
->upper
>= a
->upper
) && (b
->lower
<= a
->upper
)));
580 /* seg_over_left -- is the right edge of (a) located at or left of the right edge of (b)?
583 seg_over_left(PG_FUNCTION_ARGS
)
585 SEG
*a
= PG_GETARG_SEG_P(0);
586 SEG
*b
= PG_GETARG_SEG_P(1);
588 PG_RETURN_BOOL(a
->upper
<= b
->upper
);
591 /* seg_left -- is (a) entirely on the left of (b)?
594 seg_left(PG_FUNCTION_ARGS
)
596 SEG
*a
= PG_GETARG_SEG_P(0);
597 SEG
*b
= PG_GETARG_SEG_P(1);
599 PG_RETURN_BOOL(a
->upper
< b
->lower
);
602 /* seg_right -- is (a) entirely on the right of (b)?
605 seg_right(PG_FUNCTION_ARGS
)
607 SEG
*a
= PG_GETARG_SEG_P(0);
608 SEG
*b
= PG_GETARG_SEG_P(1);
610 PG_RETURN_BOOL(a
->lower
> b
->upper
);
613 /* seg_over_right -- is the left edge of (a) located at or right of the left edge of (b)?
616 seg_over_right(PG_FUNCTION_ARGS
)
618 SEG
*a
= PG_GETARG_SEG_P(0);
619 SEG
*b
= PG_GETARG_SEG_P(1);
621 PG_RETURN_BOOL(a
->lower
>= b
->lower
);
625 seg_union(PG_FUNCTION_ARGS
)
627 SEG
*a
= PG_GETARG_SEG_P(0);
628 SEG
*b
= PG_GETARG_SEG_P(1);
631 n
= (SEG
*) palloc(sizeof(*n
));
633 /* take max of upper endpoints */
634 if (a
->upper
> b
->upper
)
637 n
->u_sigd
= a
->u_sigd
;
643 n
->u_sigd
= b
->u_sigd
;
647 /* take min of lower endpoints */
648 if (a
->lower
< b
->lower
)
651 n
->l_sigd
= a
->l_sigd
;
657 n
->l_sigd
= b
->l_sigd
;
661 PG_RETURN_POINTER(n
);
665 seg_inter(PG_FUNCTION_ARGS
)
667 SEG
*a
= PG_GETARG_SEG_P(0);
668 SEG
*b
= PG_GETARG_SEG_P(1);
671 n
= (SEG
*) palloc(sizeof(*n
));
673 /* take min of upper endpoints */
674 if (a
->upper
< b
->upper
)
677 n
->u_sigd
= a
->u_sigd
;
683 n
->u_sigd
= b
->u_sigd
;
687 /* take max of lower endpoints */
688 if (a
->lower
> b
->lower
)
691 n
->l_sigd
= a
->l_sigd
;
697 n
->l_sigd
= b
->l_sigd
;
701 PG_RETURN_POINTER(n
);
705 rt_seg_size(SEG
*a
, float *size
)
707 if (a
== (SEG
*) NULL
|| a
->upper
<= a
->lower
)
710 *size
= fabsf(a
->upper
- a
->lower
);
714 seg_size(PG_FUNCTION_ARGS
)
716 SEG
*seg
= PG_GETARG_SEG_P(0);
718 PG_RETURN_FLOAT4(fabsf(seg
->upper
- seg
->lower
));
722 /*****************************************************************************
723 * Miscellaneous operators
724 *****************************************************************************/
726 seg_cmp(PG_FUNCTION_ARGS
)
728 SEG
*a
= PG_GETARG_SEG_P(0);
729 SEG
*b
= PG_GETARG_SEG_P(1);
732 * First compare on lower boundary position
734 if (a
->lower
< b
->lower
)
736 if (a
->lower
> b
->lower
)
740 * a->lower == b->lower, so consider type of boundary.
742 * A '-' lower bound is < any other kind (this could only be relevant if
743 * -HUGE_VAL is used as a regular data value). A '<' lower bound is < any
744 * other kind except '-'. A '>' lower bound is > any other kind.
746 if (a
->l_ext
!= b
->l_ext
)
763 * For other boundary types, consider # of significant digits first.
765 if (a
->l_sigd
< b
->l_sigd
) /* (a) is blurred and is likely to include (b) */
767 if (a
->l_sigd
> b
->l_sigd
) /* (a) is less blurred and is likely to be
772 * For same # of digits, an approximate boundary is more blurred than
775 if (a
->l_ext
!= b
->l_ext
)
777 if (a
->l_ext
== '~') /* (a) is approximate, while (b) is exact */
781 /* can't get here unless data is corrupt */
782 elog(ERROR
, "bogus lower boundary types %d %d",
783 (int) a
->l_ext
, (int) b
->l_ext
);
786 /* at this point, the lower boundaries are identical */
789 * First compare on upper boundary position
791 if (a
->upper
< b
->upper
)
793 if (a
->upper
> b
->upper
)
797 * a->upper == b->upper, so consider type of boundary.
799 * A '-' upper bound is > any other kind (this could only be relevant if
800 * HUGE_VAL is used as a regular data value). A '<' upper bound is < any
801 * other kind. A '>' upper bound is > any other kind except '-'.
803 if (a
->u_ext
!= b
->u_ext
)
820 * For other boundary types, consider # of significant digits first. Note
821 * result here is converse of the lower-boundary case.
823 if (a
->u_sigd
< b
->u_sigd
) /* (a) is blurred and is likely to include (b) */
825 if (a
->u_sigd
> b
->u_sigd
) /* (a) is less blurred and is likely to be
830 * For same # of digits, an approximate boundary is more blurred than
831 * exact. Again, result is converse of lower-boundary case.
833 if (a
->u_ext
!= b
->u_ext
)
835 if (a
->u_ext
== '~') /* (a) is approximate, while (b) is exact */
839 /* can't get here unless data is corrupt */
840 elog(ERROR
, "bogus upper boundary types %d %d",
841 (int) a
->u_ext
, (int) b
->u_ext
);
848 seg_lt(PG_FUNCTION_ARGS
)
850 int cmp
= DatumGetInt32(DirectFunctionCall2(seg_cmp
,
852 PG_GETARG_DATUM(1)));
854 PG_RETURN_BOOL(cmp
< 0);
858 seg_le(PG_FUNCTION_ARGS
)
860 int cmp
= DatumGetInt32(DirectFunctionCall2(seg_cmp
,
862 PG_GETARG_DATUM(1)));
864 PG_RETURN_BOOL(cmp
<= 0);
868 seg_gt(PG_FUNCTION_ARGS
)
870 int cmp
= DatumGetInt32(DirectFunctionCall2(seg_cmp
,
872 PG_GETARG_DATUM(1)));
874 PG_RETURN_BOOL(cmp
> 0);
878 seg_ge(PG_FUNCTION_ARGS
)
880 int cmp
= DatumGetInt32(DirectFunctionCall2(seg_cmp
,
882 PG_GETARG_DATUM(1)));
884 PG_RETURN_BOOL(cmp
>= 0);
889 seg_different(PG_FUNCTION_ARGS
)
891 int cmp
= DatumGetInt32(DirectFunctionCall2(seg_cmp
,
893 PG_GETARG_DATUM(1)));
895 PG_RETURN_BOOL(cmp
!= 0);
900 /*****************************************************************************
901 * Auxiliary functions
902 *****************************************************************************/
905 * The purpose of this routine is to print the given floating point
906 * value with exactly n significant digits. Its behaviour
907 * is similar to %.ng except it prints 8.00 where %.ng would
908 * print 8. Returns the length of the string written at "result".
910 * Caller must provide a sufficiently large result buffer; 16 bytes
911 * should be enough for all known float implementations.
914 restore(char *result
, float val
, int n
)
917 '0', '0', '0', '0', '0',
918 '0', '0', '0', '0', '0',
919 '0', '0', '0', '0', '0',
920 '0', '0', '0', '0', '0',
921 '0', '0', '0', '0', '\0'
930 * Put a cap on the number of significant digits to avoid garbage in the
931 * output and ensure we don't overrun the result buffer. (n should not be
932 * negative, but check to protect ourselves against corrupted data.)
939 /* remember the sign */
940 sign
= (val
< 0 ? 1 : 0);
942 /* print, in %e style to start with */
943 sprintf(result
, "%.*e", n
- 1, val
);
945 /* find the exponent */
946 p
= strchr(result
, 'e');
948 /* punt if we have 'inf' or similar */
950 return strlen(result
);
955 /* just truncate off the 'e+00' */
963 * remove the decimal point from the mantissa and write the digits
966 for (p
= result
+ sign
, i
= 10, dp
= 0; *p
!= 'e'; p
++, i
++)
971 dp
= i
--; /* skip the decimal point */
975 dp
= i
--; /* no decimal point was found in the above
980 if (dp
- 10 + exp
>= n
)
983 * the decimal point is behind the last significant digit;
984 * the digits in between must be converted to the exponent
985 * and the decimal point placed after the first digit
987 exp
= dp
- 10 + exp
- n
;
990 /* insert the decimal point */
994 for (i
= 23; i
> dp
; i
--)
1000 * adjust the exponent by the number of digits after the
1004 sprintf(&buf
[11 + n
], "e%d", exp
+ n
- 1);
1006 sprintf(&buf
[11], "e%d", exp
+ n
- 1);
1011 strcpy(result
, &buf
[9]);
1014 strcpy(result
, &buf
[10]);
1017 { /* insert the decimal point */
1019 for (i
= 23; i
> dp
; i
--)
1020 buf
[i
] = buf
[i
- 1];
1026 strcpy(result
, &buf
[9]);
1029 strcpy(result
, &buf
[10]);
1040 strcpy(result
, &buf
[dp
- 2]);
1043 strcpy(result
, &buf
[dp
- 1]);
1047 /* do nothing for abs(exp) > 4; %e must be OK */
1048 /* just get rid of zeroes after [eE]- and +zeroes after [Ee]. */
1050 /* ... this is not done yet. */
1052 return strlen(result
);
1060 /* find out the number of significant digits in a string representing
1061 * a floating point number
1064 significant_digits(const char *s
)
1072 /* skip leading zeroes and sign */
1073 for (c
= *p
; (c
== '0' || c
== '+' || c
== '-') && c
!= 0; c
= *(++p
));
1075 /* skip decimal point and following zeroes */
1076 for (c
= *p
; (c
== '0' || c
== '.') && c
!= 0; c
= *(++p
))
1082 /* count significant digits (n) */
1083 for (c
= *p
, n
= 0; c
!= 0; c
= *(++p
))
1085 if (!((c
>= '0' && c
<= '9') || (c
== '.')))