Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH 0/2] update decNumber for SQL
@ 2020-06-05 15:41 Chris Sosnin
  2020-06-05 15:41 ` [Tarantool-patches] [PATCH 1/2] Allow leading and trailing whitespaces in FromString Chris Sosnin
  2020-06-05 15:41 ` [Tarantool-patches] [PATCH 2/2] Add IsWhole method for checking the fractional part of a number Chris Sosnin
  0 siblings, 2 replies; 12+ messages in thread
From: Chris Sosnin @ 2020-06-05 15:41 UTC (permalink / raw)
  To: tarantool-patches, sergepetrenko, v.shpilevoy

branch: https://github.com/tarantool/decNumber/tree/ksosnin/utilities-for-sql
issue: https://github.com/tarantool/tarantool/issues/4415

Chris Sosnin (2):
  Allow leading and trailing whitespaces in FromString
  Add IsWhole method for checking the fractional part of a number

 decNumber.c | 74 ++++++++++++++++++++++++++++++++++++++++++-----------
 decNumber.h |  1 +
 2 files changed, 60 insertions(+), 15 deletions(-)

-- 
2.21.1 (Apple Git-122.3)

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [Tarantool-patches] [PATCH 1/2] Allow leading and trailing whitespaces in FromString
  2020-06-05 15:41 [Tarantool-patches] [PATCH 0/2] update decNumber for SQL Chris Sosnin
@ 2020-06-05 15:41 ` Chris Sosnin
  2020-06-06 13:48   ` Serge Petrenko
  2020-06-11 17:06   ` Vladislav Shpilevoy
  2020-06-05 15:41 ` [Tarantool-patches] [PATCH 2/2] Add IsWhole method for checking the fractional part of a number Chris Sosnin
  1 sibling, 2 replies; 12+ messages in thread
From: Chris Sosnin @ 2020-06-05 15:41 UTC (permalink / raw)
  To: tarantool-patches, sergepetrenko, v.shpilevoy

This is needed for SQL, since these whitespaces are allowed in
conversions from string.

Needed for tarantool/tarantool#4415
---
 decNumber.c | 44 +++++++++++++++++++++++++++++---------------
 1 file changed, 29 insertions(+), 15 deletions(-)

diff --git a/decNumber.c b/decNumber.c
index 85deb13..7c53bc3 100644
--- a/decNumber.c
+++ b/decNumber.c
@@ -555,9 +555,9 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
   Unit  *allocres=NULL;            // -> allocated result, iff allocated
   Int   d=0;                       // count of digits found in decimal part
   const char *dotchar=NULL;        // where dot was found
-  const char *cfirst=chars;        // -> first character of decimal part
+  const char *cfirst=NULL;         // -> first character of decimal part
   const char *last=NULL;           // -> last digit of decimal part
-  const char *c;                   // work
+  const char *c, *p;               // work
   Unit  *up;                       // ..
   #if DECDPUN>1
   Int   cut, out;                  // ..
@@ -571,6 +571,9 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
   #endif
 
   do {                             // status & malloc protection
+    for (; *chars==' '; ++chars);  // skip leading whitespaces
+    cfirst=chars;
+
     for (c=chars;; c++) {          // -> input character
       if (*c>='0' && *c<='9') {    // test for Arabic digit
         last=c;
@@ -590,13 +593,14 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
           cfirst++;
           continue;}
         }
+      for (p=c; *p==' '; ++p);
       // *c is not a digit, or a valid +, -, or '.'
       break;
       } // c
 
     if (last==NULL) {              // no digits yet
       status=DEC_Conversion_syntax;// assume the worst
-      if (*c=='\0') break;         // and no more to come...
+      if (*p=='\0') break;         // and no more to come...
       #if DECSUBSET
       // if subset then infinities and NaNs are not allowed
       if (!set->extended) break;   // hopeless
@@ -626,7 +630,8 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
       // now either nothing, or nnnn payload, expected
       // -> start of integer and skip leading 0s [including plain 0]
       for (cfirst=c; *cfirst=='0';) cfirst++;
-      if (*cfirst=='\0') {         // "NaN" or "sNaN", maybe with all 0s
+      for (p=cfirst; *p==' '; ++p);
+      if (*p=='\0') {              // "NaN" or "sNaN", maybe with all 0s
         status=0;                  // it's good
         break;                     // ..
         }
@@ -635,7 +640,8 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
         if (*c<'0' || *c>'9') break; // test for Arabic digit
         last=c;
         }
-      if (*c!='\0') break;         // not all digits
+      for (p=c; *p==' '; ++p);
+      if (*p!='\0') break;         // not all digits
       if (d>set->digits-1) {
         // [NB: payload in a decNumber can be full length unless
         // clamped, in which case can only be digits-1]
@@ -647,7 +653,7 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
       bits=dn->bits;               // for copy-back
       } // last==NULL
 
-     else if (*c!='\0') {          // more to process...
+     else if (p==c && *p!='\0') {  // more to process...
       // had some digits; exponent is only valid sequence now
       Flag nege;                   // 1=negative exponent
       const char *firstexp;        // -> first significant exponent digit
@@ -659,16 +665,18 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
       c++;                         // to (possible) sign
       if (*c=='-') {nege=1; c++;}
        else if (*c=='+') c++;
-      if (*c=='\0') break;
-
-      for (; *c=='0' && *(c+1)!='\0';) c++;  // strip insignificant zeros
+      for (p=c; *p==' '; ++p);
+      if (*p=='\0') break;
+      // strip insignificant zeros
+      for (; *c=='0' && *(c+1)!='\0' && *(c+1)!=' ';) c++;
       firstexp=c;                            // save exponent digit place
       for (; ;c++) {
         if (*c<'0' || *c>'9') break;         // not a digit
         exponent=X10(exponent)+(Int)*c-(Int)'0';
         } // c
       // if not now on a '\0', *c must not be a digit
-      if (*c!='\0') break;
+      for (p=c; *p==' '; ++p);
+      if (*p!='\0') break;
 
       // (this next test must be after the syntax checks)
       // if it was too long the exponent may have wrapped, so check
@@ -681,6 +689,11 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
       status=0;                         // is OK
       } // stuff after digits
 
+     else if (*p!='\0') {    // whitespaces in the middle
+       status=DEC_Conversion_syntax;
+       break;
+     }
+
     // Here when whole string has been inspected; syntax is good
     // cfirst->first digit (never dot), last->last digit (ditto)
 
@@ -7739,12 +7752,13 @@ static decNumber *decDecap(decNumber *dn, Int drop) {
 /*   if (decBiStr(test, "mike", "MIKE")) ...                          */
 /* ------------------------------------------------------------------ */
 static Flag decBiStr(const char *targ, const char *str1, const char *str2) {
-  for (;;targ++, str1++, str2++) {
+  const char *end=strchr(targ, ' ');
+  if (end==NULL)
+    end=targ+strlen(targ);
+  for (;targ<end; targ++, str1++, str2++) {
     if (*targ!=*str1 && *targ!=*str2) return 0;
-    // *targ has a match in one (or both, if terminator)
-    if (*targ=='\0') break;
-    } // forever
-  return 1;
+    } // for
+  return *str1=='\0';
   } // decBiStr
 
 /* ------------------------------------------------------------------ */
-- 
2.21.1 (Apple Git-122.3)

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [Tarantool-patches] [PATCH 2/2] Add IsWhole method for checking the fractional part of a number
  2020-06-05 15:41 [Tarantool-patches] [PATCH 0/2] update decNumber for SQL Chris Sosnin
  2020-06-05 15:41 ` [Tarantool-patches] [PATCH 1/2] Allow leading and trailing whitespaces in FromString Chris Sosnin
@ 2020-06-05 15:41 ` Chris Sosnin
  2020-06-06 14:17   ` Serge Petrenko
  2020-06-11 17:06   ` Vladislav Shpilevoy
  1 sibling, 2 replies; 12+ messages in thread
From: Chris Sosnin @ 2020-06-05 15:41 UTC (permalink / raw)
  To: tarantool-patches, sergepetrenko, v.shpilevoy

Currently there is no efficient way to do this.

Needed for tarantool/tarantool#4415
---
 decNumber.c | 30 ++++++++++++++++++++++++++++++
 decNumber.h |  1 +
 2 files changed, 31 insertions(+)

diff --git a/decNumber.c b/decNumber.c
index 7c53bc3..f4ad927 100644
--- a/decNumber.c
+++ b/decNumber.c
@@ -501,6 +501,36 @@ uLong decNumberToUInt64(const decNumber *dn, decContext *set) {
   return 0;
   }  // decNumberToUInt64
 
+Flag decNumberIsWhole(const decNumber *dn) {
+  const Unit *up=dn->lsu;
+  if (dn->exponent>=0) {
+    return 1;
+    }
+   else {
+    Int count=-dn->exponent;
+    // spin up whole units until reach the Unit with the unit digit
+    for (; count>=DECDPUN; up++) {
+      if (*up!=0) return 0;
+      count-=DECDPUN;
+      }
+    if (count==0) return 1;             // [a multiple of DECDPUN]
+     else {                             // [not multiple of DECDPUN]
+      Int rem, theInt=0;                          // work
+      // slice off fraction digits and check for non-zero
+      #if DECDPUN<=4
+        theInt=QUOT10(*up, count);
+        rem=*up-theInt*powers[count];
+      #else
+        rem=*up%powers[count];          // slice off discards
+        theInt=*up/powers[count];
+      #endif
+      if (rem!=0) return 0;
+      up++;
+      }
+    }
+    return 1;
+  }  // decNumberIsWhole
+
 /* ------------------------------------------------------------------ */
 /* to-scientific-string -- conversion to numeric string               */
 /* to-engineering-string -- conversion to numeric string              */
diff --git a/decNumber.h b/decNumber.h
index 85a3f3f..71ab27f 100644
--- a/decNumber.h
+++ b/decNumber.h
@@ -169,6 +169,7 @@
   decNumber  * decNumberTrim(decNumber *);
   const char * decNumberVersion(void);
   decNumber  * decNumberZero(decNumber *);
+  uint8_t      decNumberIsWhole(const decNumber *dn);
 
   /* Functions for testing decNumbers (normality depends on context)  */
   int32_t decNumberIsNormal(const decNumber *, decContext *);
-- 
2.21.1 (Apple Git-122.3)

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Tarantool-patches] [PATCH 1/2] Allow leading and trailing whitespaces in FromString
  2020-06-05 15:41 ` [Tarantool-patches] [PATCH 1/2] Allow leading and trailing whitespaces in FromString Chris Sosnin
@ 2020-06-06 13:48   ` Serge Petrenko
  2020-06-07 12:38     ` Chris Sosnin
  2020-06-11 17:06   ` Vladislav Shpilevoy
  1 sibling, 1 reply; 12+ messages in thread
From: Serge Petrenko @ 2020-06-06 13:48 UTC (permalink / raw)
  To: Chris Sosnin; +Cc: tarantool-patches, v.shpilevoy


05.06.2020 18:41, Chris Sosnin пишет:
> This is needed for SQL, since these whitespaces are allowed in
> conversions from string.
>
> Needed for tarantool/tarantool#4415

Hi! Thanks for the patch!

See one comment below.

> ---
>   decNumber.c | 44 +++++++++++++++++++++++++++++---------------
>   1 file changed, 29 insertions(+), 15 deletions(-)
>
> diff --git a/decNumber.c b/decNumber.c
> index 85deb13..7c53bc3 100644
> --- a/decNumber.c
> +++ b/decNumber.c
> @@ -555,9 +555,9 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>     Unit  *allocres=NULL;            // -> allocated result, iff allocated
>     Int   d=0;                       // count of digits found in decimal part
>     const char *dotchar=NULL;        // where dot was found
> -  const char *cfirst=chars;        // -> first character of decimal part
> +  const char *cfirst=NULL;         // -> first character of decimal part
>     const char *last=NULL;           // -> last digit of decimal part
> -  const char *c;                   // work
> +  const char *c, *p;               // work
>     Unit  *up;                       // ..
>     #if DECDPUN>1
>     Int   cut, out;                  // ..
> @@ -571,6 +571,9 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>     #endif
>   
>     do {                             // status & malloc protection
> +    for (; *chars==' '; ++chars);  // skip leading whitespaces
> +    cfirst=chars;
> +
>       for (c=chars;; c++) {          // -> input character
>         if (*c>='0' && *c<='9') {    // test for Arabic digit
>           last=c;
> @@ -590,13 +593,14 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>             cfirst++;
>             continue;}
>           }
> +      for (p=c; *p==' '; ++p);
Looks like this `for` loop should go below.
>         // *c is not a digit, or a valid +, -, or '.'
>         break;
>         } // c
>   

Here, ^ (This guy's formatting is confusing as hell, I know)

>       if (last==NULL) {              // no digits yet
>         status=DEC_Conversion_syntax;// assume the worst
> -      if (*c=='\0') break;         // and no more to come...
> +      if (*p=='\0') break;         // and no more to come...
>         #if DECSUBSET
>         // if subset then infinities and NaNs are not allowed
>         if (!set->extended) break;   // hopeless
> @@ -626,7 +630,8 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>         // now either nothing, or nnnn payload, expected
>         // -> start of integer and skip leading 0s [including plain 0]
>         for (cfirst=c; *cfirst=='0';) cfirst++;
> -      if (*cfirst=='\0') {         // "NaN" or "sNaN", maybe with all 0s
> +      for (p=cfirst; *p==' '; ++p);
> +      if (*p=='\0') {              // "NaN" or "sNaN", maybe with all 0s
>           status=0;                  // it's good
>           break;                     // ..
>           }
> @@ -635,7 +640,8 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>           if (*c<'0' || *c>'9') break; // test for Arabic digit
>           last=c;
>           }
> -      if (*c!='\0') break;         // not all digits
> +      for (p=c; *p==' '; ++p);
> +      if (*p!='\0') break;         // not all digits
>         if (d>set->digits-1) {
>           // [NB: payload in a decNumber can be full length unless
>           // clamped, in which case can only be digits-1]
> @@ -647,7 +653,7 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>         bits=dn->bits;               // for copy-back
>         } // last==NULL
>   
> -     else if (*c!='\0') {          // more to process...
> +     else if (p==c && *p!='\0') {  // more to process...
>         // had some digits; exponent is only valid sequence now
>         Flag nege;                   // 1=negative exponent
>         const char *firstexp;        // -> first significant exponent digit
> @@ -659,16 +665,18 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>         c++;                         // to (possible) sign
>         if (*c=='-') {nege=1; c++;}
>          else if (*c=='+') c++;
> -      if (*c=='\0') break;
> -
> -      for (; *c=='0' && *(c+1)!='\0';) c++;  // strip insignificant zeros
> +      for (p=c; *p==' '; ++p);
> +      if (*p=='\0') break;
> +      // strip insignificant zeros
> +      for (; *c=='0' && *(c+1)!='\0' && *(c+1)!=' ';) c++;
>         firstexp=c;                            // save exponent digit place
>         for (; ;c++) {
>           if (*c<'0' || *c>'9') break;         // not a digit
>           exponent=X10(exponent)+(Int)*c-(Int)'0';
>           } // c
>         // if not now on a '\0', *c must not be a digit
> -      if (*c!='\0') break;
> +      for (p=c; *p==' '; ++p);
> +      if (*p!='\0') break;
>   
>         // (this next test must be after the syntax checks)
>         // if it was too long the exponent may have wrapped, so check
> @@ -681,6 +689,11 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>         status=0;                         // is OK
>         } // stuff after digits
>   
> +     else if (*p!='\0') {    // whitespaces in the middle
> +       status=DEC_Conversion_syntax;
> +       break;
> +     }
> +
>       // Here when whole string has been inspected; syntax is good
>       // cfirst->first digit (never dot), last->last digit (ditto)
>   
> @@ -7739,12 +7752,13 @@ static decNumber *decDecap(decNumber *dn, Int drop) {
>   /*   if (decBiStr(test, "mike", "MIKE")) ...                          */
>   /* ------------------------------------------------------------------ */
>   static Flag decBiStr(const char *targ, const char *str1, const char *str2) {
> -  for (;;targ++, str1++, str2++) {
> +  const char *end=strchr(targ, ' ');
> +  if (end==NULL)
> +    end=targ+strlen(targ);
> +  for (;targ<end; targ++, str1++, str2++) {
>       if (*targ!=*str1 && *targ!=*str2) return 0;
> -    // *targ has a match in one (or both, if terminator)
> -    if (*targ=='\0') break;
> -    } // forever
> -  return 1;
> +    } // for
> +  return *str1=='\0';
>     } // decBiStr
>   
>   /* ------------------------------------------------------------------ */

-- 
Serge Petrenko

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Tarantool-patches] [PATCH 2/2] Add IsWhole method for checking the fractional part of a number
  2020-06-05 15:41 ` [Tarantool-patches] [PATCH 2/2] Add IsWhole method for checking the fractional part of a number Chris Sosnin
@ 2020-06-06 14:17   ` Serge Petrenko
  2020-06-07 12:40     ` Chris Sosnin
  2020-06-11 17:06   ` Vladislav Shpilevoy
  1 sibling, 1 reply; 12+ messages in thread
From: Serge Petrenko @ 2020-06-06 14:17 UTC (permalink / raw)
  To: Chris Sosnin; +Cc: tarantool-patches, v.shpilevoy

05.06.2020 18:41, Chris Sosnin пишет:
> Currently there is no efficient way to do this.
>
> Needed for tarantool/tarantool#4415
Thanks for the patch!
> ---
>   decNumber.c | 30 ++++++++++++++++++++++++++++++
>   decNumber.h |  1 +
>   2 files changed, 31 insertions(+)
>
> diff --git a/decNumber.c b/decNumber.c
> index 7c53bc3..f4ad927 100644
> --- a/decNumber.c
> +++ b/decNumber.c
> @@ -501,6 +501,36 @@ uLong decNumberToUInt64(const decNumber *dn, decContext *set) {
>     return 0;
>     }  // decNumberToUInt64
>   
> +Flag decNumberIsWhole(const decNumber *dn) {
> +  const Unit *up=dn->lsu;
> +  if (dn->exponent>=0) {
> +    return 1;
> +    }
> +   else {
> +    Int count=-dn->exponent;
> +    // spin up whole units until reach the Unit with the unit digit
> +    for (; count>=DECDPUN; up++) {
> +      if (*up!=0) return 0;
> +      count-=DECDPUN;
> +      }
> +    if (count==0) return 1;             // [a multiple of DECDPUN]
> +     else {                             // [not multiple of DECDPUN]
> +      Int rem, theInt=0;                          // work
> +      // slice off fraction digits and check for non-zero
> +      #if DECDPUN<=4
> +        theInt=QUOT10(*up, count);
> +        rem=*up-theInt*powers[count];
> +      #else
> +        rem=*up%powers[count];          // slice off discards
> +        theInt=*up/powers[count];
> +      #endif
> +      if (rem!=0) return 0;
> +      up++;
> +      }
> +    }
> +    return 1;
> +  }  // decNumberIsWhole
> +

Consider this  diff:

diff --git a/decNumber.c b/decNumber.c
index f4ad927..acc3fb1 100644
--- a/decNumber.c
+++ b/decNumber.c
@@ -515,17 +515,14 @@ Flag decNumberIsWhole(const decNumber *dn) {
        }
      if (count==0) return 1;             // [a multiple of DECDPUN]
       else {                             // [not multiple of DECDPUN]
-      Int rem, theInt=0;                          // work
+      Int rem;                          // work
        // slice off fraction digits and check for non-zero
        #if DECDPUN<=4
-        theInt=QUOT10(*up, count);
-        rem=*up-theInt*powers[count];
+        rem=*up-QUOT10(*up, count)*powers[count];
        #else
          rem=*up%powers[count];          // slice off discards
-        theInt=*up/powers[count];
        #endif
        if (rem!=0) return 0;
-      up++;
        }
      }
      return 1;

>   /* ------------------------------------------------------------------ */
>   /* to-scientific-string -- conversion to numeric string               */
>   /* to-engineering-string -- conversion to numeric string              */
> diff --git a/decNumber.h b/decNumber.h
> index 85a3f3f..71ab27f 100644
> --- a/decNumber.h
> +++ b/decNumber.h
> @@ -169,6 +169,7 @@
>     decNumber  * decNumberTrim(decNumber *);
>     const char * decNumberVersion(void);
>     decNumber  * decNumberZero(decNumber *);
> +  uint8_t      decNumberIsWhole(const decNumber *dn);
>   
>     /* Functions for testing decNumbers (normality depends on context)  */
>     int32_t decNumberIsNormal(const decNumber *, decContext *);

-- 
Serge Petrenko

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Tarantool-patches] [PATCH 1/2] Allow leading and trailing whitespaces in FromString
  2020-06-06 13:48   ` Serge Petrenko
@ 2020-06-07 12:38     ` Chris Sosnin
  0 siblings, 0 replies; 12+ messages in thread
From: Chris Sosnin @ 2020-06-07 12:38 UTC (permalink / raw)
  To: Serge Petrenko; +Cc: tarantool-patches, Vladislav Shpilevoy

Hi! Thank you for the review!

> On 6 Jun 2020, at 16:48, Serge Petrenko <sergepetrenko@tarantool.org> wrote:
> 
> 
> 05.06.2020 18:41, Chris Sosnin пишет:
>> This is needed for SQL, since these whitespaces are allowed in
>> conversions from string.
>> 
>> Needed for tarantool/tarantool#4415
> 
> Hi! Thanks for the patch!
> 
> See one comment below.
> 
>> ---
>>  decNumber.c | 44 +++++++++++++++++++++++++++++---------------
>>  1 file changed, 29 insertions(+), 15 deletions(-)
>> 
>> diff --git a/decNumber.c b/decNumber.c
>> index 85deb13..7c53bc3 100644
>> --- a/decNumber.c
>> +++ b/decNumber.c
>> @@ -555,9 +555,9 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>>    Unit  *allocres=NULL;            // -> allocated result, iff allocated
>>    Int   d=0;                       // count of digits found in decimal part
>>    const char *dotchar=NULL;        // where dot was found
>> -  const char *cfirst=chars;        // -> first character of decimal part
>> +  const char *cfirst=NULL;         // -> first character of decimal part
>>    const char *last=NULL;           // -> last digit of decimal part
>> -  const char *c;                   // work
>> +  const char *c, *p;               // work
>>    Unit  *up;                       // ..
>>    #if DECDPUN>1
>>    Int   cut, out;                  // ..
>> @@ -571,6 +571,9 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>>    #endif
>>      do {                             // status & malloc protection
>> +    for (; *chars==' '; ++chars);  // skip leading whitespaces
>> +    cfirst=chars;
>> +
>>      for (c=chars;; c++) {          // -> input character
>>        if (*c>='0' && *c<='9') {    // test for Arabic digit
>>          last=c;
>> @@ -590,13 +593,14 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>>            cfirst++;
>>            continue;}
>>          }
>> +      for (p=c; *p==' '; ++p);
> Looks like this `for` loop should go below.
>>        // *c is not a digit, or a valid +, -, or '.'
>>        break;
>>        } // c
>>  
> 
> Here, ^ (This guy's formatting is confusing as hell, I know)

You are right, thank you for the catch!
I applied the change on the branch.

> 
>>      if (last==NULL) {              // no digits yet
>>        status=DEC_Conversion_syntax;// assume the worst
>> -      if (*c=='\0') break;         // and no more to come...
>> +      if (*p=='\0') break;         // and no more to come...
>>        #if DECSUBSET
>>        // if subset then infinities and NaNs are not allowed
>>        if (!set->extended) break;   // hopeless
>> @@ -626,7 +630,8 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>>        // now either nothing, or nnnn payload, expected
>>        // -> start of integer and skip leading 0s [including plain 0]
>>        for (cfirst=c; *cfirst=='0';) cfirst++;
>> -      if (*cfirst=='\0') {         // "NaN" or "sNaN", maybe with all 0s
>> +      for (p=cfirst; *p==' '; ++p);
>> +      if (*p=='\0') {              // "NaN" or "sNaN", maybe with all 0s
>>          status=0;                  // it's good
>>          break;                     // ..
>>          }
>> @@ -635,7 +640,8 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>>          if (*c<'0' || *c>'9') break; // test for Arabic digit
>>          last=c;
>>          }
>> -      if (*c!='\0') break;         // not all digits
>> +      for (p=c; *p==' '; ++p);
>> +      if (*p!='\0') break;         // not all digits
>>        if (d>set->digits-1) {
>>          // [NB: payload in a decNumber can be full length unless
>>          // clamped, in which case can only be digits-1]
>> @@ -647,7 +653,7 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>>        bits=dn->bits;               // for copy-back
>>        } // last==NULL
>>  -     else if (*c!='\0') {          // more to process...
>> +     else if (p==c && *p!='\0') {  // more to process...
>>        // had some digits; exponent is only valid sequence now
>>        Flag nege;                   // 1=negative exponent
>>        const char *firstexp;        // -> first significant exponent digit
>> @@ -659,16 +665,18 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>>        c++;                         // to (possible) sign
>>        if (*c=='-') {nege=1; c++;}
>>         else if (*c=='+') c++;
>> -      if (*c=='\0') break;
>> -
>> -      for (; *c=='0' && *(c+1)!='\0';) c++;  // strip insignificant zeros
>> +      for (p=c; *p==' '; ++p);
>> +      if (*p=='\0') break;
>> +      // strip insignificant zeros
>> +      for (; *c=='0' && *(c+1)!='\0' && *(c+1)!=' ';) c++;
>>        firstexp=c;                            // save exponent digit place
>>        for (; ;c++) {
>>          if (*c<'0' || *c>'9') break;         // not a digit
>>          exponent=X10(exponent)+(Int)*c-(Int)'0';
>>          } // c
>>        // if not now on a '\0', *c must not be a digit
>> -      if (*c!='\0') break;
>> +      for (p=c; *p==' '; ++p);
>> +      if (*p!='\0') break;
>>          // (this next test must be after the syntax checks)
>>        // if it was too long the exponent may have wrapped, so check
>> @@ -681,6 +689,11 @@ decNumber * decNumberFromString(decNumber *dn, const char chars[],
>>        status=0;                         // is OK
>>        } // stuff after digits
>>  +     else if (*p!='\0') {    // whitespaces in the middle
>> +       status=DEC_Conversion_syntax;
>> +       break;
>> +     }
>> +
>>      // Here when whole string has been inspected; syntax is good
>>      // cfirst->first digit (never dot), last->last digit (ditto)
>>  @@ -7739,12 +7752,13 @@ static decNumber *decDecap(decNumber *dn, Int drop) {
>>  /*   if (decBiStr(test, "mike", "MIKE")) ...                          */
>>  /* ------------------------------------------------------------------ */
>>  static Flag decBiStr(const char *targ, const char *str1, const char *str2) {
>> -  for (;;targ++, str1++, str2++) {
>> +  const char *end=strchr(targ, ' ');
>> +  if (end==NULL)
>> +    end=targ+strlen(targ);
>> +  for (;targ<end; targ++, str1++, str2++) {
>>      if (*targ!=*str1 && *targ!=*str2) return 0;
>> -    // *targ has a match in one (or both, if terminator)
>> -    if (*targ=='\0') break;
>> -    } // forever
>> -  return 1;
>> +    } // for
>> +  return *str1=='\0';
>>    } // decBiStr
>>    /* ------------------------------------------------------------------ */
> 
> -- 
> Serge Petrenko

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Tarantool-patches] [PATCH 2/2] Add IsWhole method for checking the fractional part of a number
  2020-06-06 14:17   ` Serge Petrenko
@ 2020-06-07 12:40     ` Chris Sosnin
  2020-06-08  8:59       ` Serge Petrenko
  0 siblings, 1 reply; 12+ messages in thread
From: Chris Sosnin @ 2020-06-07 12:40 UTC (permalink / raw)
  To: Serge Petrenko; +Cc: tarantool-patches, Vladislav Shpilevoy

Thank you for the review!

> On 6 Jun 2020, at 17:17, Serge Petrenko <sergepetrenko@tarantool.org> wrote:
> 
> 05.06.2020 18:41, Chris Sosnin пишет:
>> Currently there is no efficient way to do this.
>> 
>> Needed for tarantool/tarantool#4415
> Thanks for the patch!
>> ---
>>  decNumber.c | 30 ++++++++++++++++++++++++++++++
>>  decNumber.h |  1 +
>>  2 files changed, 31 insertions(+)
>> 
>> diff --git a/decNumber.c b/decNumber.c
>> index 7c53bc3..f4ad927 100644
>> --- a/decNumber.c
>> +++ b/decNumber.c
>> @@ -501,6 +501,36 @@ uLong decNumberToUInt64(const decNumber *dn, decContext *set) {
>>    return 0;
>>    }  // decNumberToUInt64
>>  +Flag decNumberIsWhole(const decNumber *dn) {
>> +  const Unit *up=dn->lsu;
>> +  if (dn->exponent>=0) {
>> +    return 1;
>> +    }
>> +   else {
>> +    Int count=-dn->exponent;
>> +    // spin up whole units until reach the Unit with the unit digit
>> +    for (; count>=DECDPUN; up++) {
>> +      if (*up!=0) return 0;
>> +      count-=DECDPUN;
>> +      }
>> +    if (count==0) return 1;             // [a multiple of DECDPUN]
>> +     else {                             // [not multiple of DECDPUN]
>> +      Int rem, theInt=0;                          // work
>> +      // slice off fraction digits and check for non-zero
>> +      #if DECDPUN<=4
>> +        theInt=QUOT10(*up, count);
>> +        rem=*up-theInt*powers[count];
>> +      #else
>> +        rem=*up%powers[count];          // slice off discards
>> +        theInt=*up/powers[count];
>> +      #endif
>> +      if (rem!=0) return 0;
>> +      up++;
>> +      }
>> +    }
>> +    return 1;
>> +  }  // decNumberIsWhole
>> +
> 
> Consider this  diff:
> 
> diff --git a/decNumber.c b/decNumber.c
> index f4ad927..acc3fb1 100644
> --- a/decNumber.c
> +++ b/decNumber.c
> @@ -515,17 +515,14 @@ Flag decNumberIsWhole(const decNumber *dn) {
>        }
>      if (count==0) return 1;             // [a multiple of DECDPUN]
>       else {                             // [not multiple of DECDPUN]
> -      Int rem, theInt=0;                          // work
> +      Int rem;                          // work
>        // slice off fraction digits and check for non-zero
>        #if DECDPUN<=4
> -        theInt=QUOT10(*up, count);
> -        rem=*up-theInt*powers[count];
> +        rem=*up-QUOT10(*up, count)*powers[count];
>        #else
>          rem=*up%powers[count];          // slice off discards
> -        theInt=*up/powers[count];
>        #endif
>        if (rem!=0) return 0;
> -      up++;
>        }
>      }
>      return 1;

I agree, looks much better, thanks!
Applied on the branch.

> 
>>  /* ------------------------------------------------------------------ */
>>  /* to-scientific-string -- conversion to numeric string               */
>>  /* to-engineering-string -- conversion to numeric string              */
>> diff --git a/decNumber.h b/decNumber.h
>> index 85a3f3f..71ab27f 100644
>> --- a/decNumber.h
>> +++ b/decNumber.h
>> @@ -169,6 +169,7 @@
>>    decNumber  * decNumberTrim(decNumber *);
>>    const char * decNumberVersion(void);
>>    decNumber  * decNumberZero(decNumber *);
>> +  uint8_t      decNumberIsWhole(const decNumber *dn);
>>      /* Functions for testing decNumbers (normality depends on context)  */
>>    int32_t decNumberIsNormal(const decNumber *, decContext *);
> 
> -- 
> Serge Petrenko

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Tarantool-patches] [PATCH 2/2] Add IsWhole method for checking the fractional part of a number
  2020-06-07 12:40     ` Chris Sosnin
@ 2020-06-08  8:59       ` Serge Petrenko
  0 siblings, 0 replies; 12+ messages in thread
From: Serge Petrenko @ 2020-06-08  8:59 UTC (permalink / raw)
  To: Chris Sosnin; +Cc: tarantool-patches, Vladislav Shpilevoy


07.06.2020 15:40, Chris Sosnin пишет:
> Thank you for the review!
>
>> On 6 Jun 2020, at 17:17, Serge Petrenko <sergepetrenko@tarantool.org> wrote:
>>
>> 05.06.2020 18:41, Chris Sosnin пишет:
>>> Currently there is no efficient way to do this.
>>>
>>> Needed for tarantool/tarantool#4415
>> Thanks for the patch!
>>> ---
>>>   decNumber.c | 30 ++++++++++++++++++++++++++++++
>>>   decNumber.h |  1 +
>>>   2 files changed, 31 insertions(+)
>>>
>>> diff --git a/decNumber.c b/decNumber.c
>>> index 7c53bc3..f4ad927 100644
>>> --- a/decNumber.c
>>> +++ b/decNumber.c
>>> @@ -501,6 +501,36 @@ uLong decNumberToUInt64(const decNumber *dn, decContext *set) {
>>>     return 0;
>>>     }  // decNumberToUInt64
>>>   +Flag decNumberIsWhole(const decNumber *dn) {
>>> +  const Unit *up=dn->lsu;
>>> +  if (dn->exponent>=0) {
>>> +    return 1;
>>> +    }
>>> +   else {
>>> +    Int count=-dn->exponent;
>>> +    // spin up whole units until reach the Unit with the unit digit
>>> +    for (; count>=DECDPUN; up++) {
>>> +      if (*up!=0) return 0;
>>> +      count-=DECDPUN;
>>> +      }
>>> +    if (count==0) return 1;             // [a multiple of DECDPUN]
>>> +     else {                             // [not multiple of DECDPUN]
>>> +      Int rem, theInt=0;                          // work
>>> +      // slice off fraction digits and check for non-zero
>>> +      #if DECDPUN<=4
>>> +        theInt=QUOT10(*up, count);
>>> +        rem=*up-theInt*powers[count];
>>> +      #else
>>> +        rem=*up%powers[count];          // slice off discards
>>> +        theInt=*up/powers[count];
>>> +      #endif
>>> +      if (rem!=0) return 0;
>>> +      up++;
>>> +      }
>>> +    }
>>> +    return 1;
>>> +  }  // decNumberIsWhole
>>> +
>> Consider this  diff:
>>
>> diff --git a/decNumber.c b/decNumber.c
>> index f4ad927..acc3fb1 100644
>> --- a/decNumber.c
>> +++ b/decNumber.c
>> @@ -515,17 +515,14 @@ Flag decNumberIsWhole(const decNumber *dn) {
>>         }
>>       if (count==0) return 1;             // [a multiple of DECDPUN]
>>        else {                             // [not multiple of DECDPUN]
>> -      Int rem, theInt=0;                          // work
>> +      Int rem;                          // work
>>         // slice off fraction digits and check for non-zero
>>         #if DECDPUN<=4
>> -        theInt=QUOT10(*up, count);
>> -        rem=*up-theInt*powers[count];
>> +        rem=*up-QUOT10(*up, count)*powers[count];
>>         #else
>>           rem=*up%powers[count];          // slice off discards
>> -        theInt=*up/powers[count];
>>         #endif
>>         if (rem!=0) return 0;
>> -      up++;
>>         }
>>       }
>>       return 1;
> I agree, looks much better, thanks!
> Applied on the branch.

Thanks for the fixes! Both patches LGTM.

>
>>>   /* ------------------------------------------------------------------ */
>>>   /* to-scientific-string -- conversion to numeric string               */
>>>   /* to-engineering-string -- conversion to numeric string              */
>>> diff --git a/decNumber.h b/decNumber.h
>>> index 85a3f3f..71ab27f 100644
>>> --- a/decNumber.h
>>> +++ b/decNumber.h
>>> @@ -169,6 +169,7 @@
>>>     decNumber  * decNumberTrim(decNumber *);
>>>     const char * decNumberVersion(void);
>>>     decNumber  * decNumberZero(decNumber *);
>>> +  uint8_t      decNumberIsWhole(const decNumber *dn);
>>>       /* Functions for testing decNumbers (normality depends on context)  */
>>>     int32_t decNumberIsNormal(const decNumber *, decContext *);
>> -- 
>> Serge Petrenko

-- 
Serge Petrenko

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Tarantool-patches] [PATCH 1/2] Allow leading and trailing whitespaces in FromString
  2020-06-05 15:41 ` [Tarantool-patches] [PATCH 1/2] Allow leading and trailing whitespaces in FromString Chris Sosnin
  2020-06-06 13:48   ` Serge Petrenko
@ 2020-06-11 17:06   ` Vladislav Shpilevoy
  2020-06-11 20:14     ` Chris Sosnin
  1 sibling, 1 reply; 12+ messages in thread
From: Vladislav Shpilevoy @ 2020-06-11 17:06 UTC (permalink / raw)
  To: Chris Sosnin, tarantool-patches, sergepetrenko

Hi! Thanks for the patch!

I am afraid this may be a dead end. You didn't check for tabs, new lines,
\r, and whatever else can be treated as a whitespace. Moreover, for some other
code it may be necessary not to skip whitespaces. Why can't you trim whitespace
symbols in SQL code? sql_atoi64(), for example, trims them, before calling
strtoll()/strtoull().

To trim right spaces you could make this function return a position where
did it stop, so as to ensure that all the rest is whitespaces, in SQL code.
That also would be consistent with strtoll()/strtoull().

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Tarantool-patches] [PATCH 2/2] Add IsWhole method for checking the fractional part of a number
  2020-06-05 15:41 ` [Tarantool-patches] [PATCH 2/2] Add IsWhole method for checking the fractional part of a number Chris Sosnin
  2020-06-06 14:17   ` Serge Petrenko
@ 2020-06-11 17:06   ` Vladislav Shpilevoy
  2020-06-11 20:30     ` Chris Sosnin
  1 sibling, 1 reply; 12+ messages in thread
From: Vladislav Shpilevoy @ 2020-06-11 17:06 UTC (permalink / raw)
  To: Chris Sosnin, tarantool-patches, sergepetrenko

I guess you rather need name IsInt. 'Whole' has a different
meaning.

Why do you need it, btw? Why decNumberToInt64() and decNumberToUInt64()
are not enough? The only usecase for this new function I can
imagine is an attempt to shrink a decimal value into a normal integer.
Which are either int64_t or uint64_t.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Tarantool-patches] [PATCH 1/2] Allow leading and trailing whitespaces in FromString
  2020-06-11 17:06   ` Vladislav Shpilevoy
@ 2020-06-11 20:14     ` Chris Sosnin
  0 siblings, 0 replies; 12+ messages in thread
From: Chris Sosnin @ 2020-06-11 20:14 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

Hi! Thank you for the review!

> On 11 Jun 2020, at 20:06, Vladislav Shpilevoy <v.shpilevoy@tarantool.org> wrote:
> 
> Hi! Thanks for the patch!
> 
> I am afraid this may be a dead end. You didn't check for tabs, new lines,
> \r, and whatever else can be treated as a whitespace. Moreover, for some other
> code it may be necessary not to skip whitespaces. Why can't you trim whitespace
> symbols in SQL code? sql_atoi64(), for example, trims them, before calling
> strtoll()/strtoull().

I made this change here to unify the behavior, but perhaps this makes sense only in SQL?
In this case I can make a wrapper similar to sql_atoi64() you mentioned. And if I understand
correctly, I should use isspace instead of char comparison.

> 
> To trim right spaces you could make this function return a position where
> did it stop, so as to ensure that all the rest is whitespaces, in SQL code.
> That also would be consistent with strtoll()/strtoull().

I like this idea, will work on it in v2.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Tarantool-patches] [PATCH 2/2] Add IsWhole method for checking the fractional part of a number
  2020-06-11 17:06   ` Vladislav Shpilevoy
@ 2020-06-11 20:30     ` Chris Sosnin
  0 siblings, 0 replies; 12+ messages in thread
From: Chris Sosnin @ 2020-06-11 20:30 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

Thank you for the review!

> On 11 Jun 2020, at 20:06, Vladislav Shpilevoy <v.shpilevoy@tarantool.org> wrote:
> 
> I guess you rather need name IsInt. 'Whole' has a different
> meaning.

You can say “whole number” — a number without decimal part.
But IsInt sounds better, I agree.

> 
> Why do you need it, btw? Why decNumberToInt64() and decNumberToUInt64()
> are not enough? The only usecase for this new function I can
> imagine is an attempt to shrink a decimal value into a normal integer.
> Which are either int64_t or uint64_t.

In case of doubles sqlVdbeIntValue returns 0 if an argument has no decimal part (and doesn’t overflow) and not 0 otherwise, but it always modifies a pointer to an integer, because some functions may still use the result of conversion. So I tried to make the behavior the same.

decimal_to_* functions always trim the value and don’t perform the check cause of decimal_to_integer call, decNumber ones require context (these also don’t really check for zero-decimal part — instead comparing exponent with 0, making 1.000 a bad argument), so we need new wrappers which will do almost the same thing but with an extra check.

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2020-06-11 20:30 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-05 15:41 [Tarantool-patches] [PATCH 0/2] update decNumber for SQL Chris Sosnin
2020-06-05 15:41 ` [Tarantool-patches] [PATCH 1/2] Allow leading and trailing whitespaces in FromString Chris Sosnin
2020-06-06 13:48   ` Serge Petrenko
2020-06-07 12:38     ` Chris Sosnin
2020-06-11 17:06   ` Vladislav Shpilevoy
2020-06-11 20:14     ` Chris Sosnin
2020-06-05 15:41 ` [Tarantool-patches] [PATCH 2/2] Add IsWhole method for checking the fractional part of a number Chris Sosnin
2020-06-06 14:17   ` Serge Petrenko
2020-06-07 12:40     ` Chris Sosnin
2020-06-08  8:59       ` Serge Petrenko
2020-06-11 17:06   ` Vladislav Shpilevoy
2020-06-11 20:30     ` Chris Sosnin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox