99#include < cassert>
1010#include < iomanip>
1111#include < utf8.h>
12+ #include < iostream>
1213
1314#include " global.h"
1415
@@ -41,14 +42,28 @@ class LIBSCRATCHCPP_EXPORT Value
4142 Value (float numberValue) :
4243 m_type (Type::Double)
4344 {
44- m_doubleValue = floatToDouble (numberValue);
45+ if (isInf (numberValue))
46+ m_type = Type::Infinity;
47+ else if (isNegativeInf (numberValue))
48+ m_type = Type::NegativeInfinity;
49+ else if (std::isnan (numberValue))
50+ m_type = Type::NaN;
51+ else
52+ m_doubleValue = floatToDouble (numberValue);
4553 }
4654
4755 /* ! Constructs a number Value. */
4856 Value (double numberValue) :
4957 m_type (Type::Double)
5058 {
51- m_doubleValue = numberValue;
59+ if (isInf (numberValue))
60+ m_type = Type::Infinity;
61+ else if (isNegativeInf (numberValue))
62+ m_type = Type::NegativeInfinity;
63+ else if (std::isnan (numberValue))
64+ m_type = Type::NaN;
65+ else
66+ m_doubleValue = numberValue;
5267 }
5368
5469 /* ! Constructs a number Value. */
@@ -83,8 +98,14 @@ class LIBSCRATCHCPP_EXPORT Value
8398 Value (const std::string &stringValue) :
8499 m_type (Type::String)
85100 {
86- new (&m_stringValue) std::string (stringValue);
87- initString (stringValue);
101+ if (stringValue == " Infinity" )
102+ m_type = Type::Infinity;
103+ else if (stringValue == " -Infinity" )
104+ m_type = Type::NegativeInfinity;
105+ else if (stringValue == " NaN" )
106+ m_type = Type::NaN;
107+ else
108+ new (&m_stringValue) std::string (stringValue);
88109 }
89110
90111 /* ! Constructs a string Value. */
@@ -140,17 +161,81 @@ class LIBSCRATCHCPP_EXPORT Value
140161 Type type () const { return m_type; }
141162
142163 /* ! Returns true if the value is infinity. */
143- bool isInfinity () const { return m_type == Type::Infinity; }
164+ bool isInfinity () const
165+ {
166+ switch (m_type) {
167+ case Type::Infinity:
168+ return true ;
169+ case Type::Integer:
170+ return isInf (m_intValue);
171+ case Type::Double:
172+ return isInf (m_doubleValue);
173+ case Type::String:
174+ return m_stringValue == " Infinity" ;
175+ default :
176+ return false ;
177+ }
178+ }
144179
145180 /* ! Returns true if the value is negative infinity. */
146- bool isNegativeInfinity () const { return m_type == Type::NegativeInfinity; }
181+ bool isNegativeInfinity () const
182+ {
183+ switch (m_type) {
184+ case Type::NegativeInfinity:
185+ return true ;
186+ case Type::Integer:
187+ return isNegativeInf (-m_intValue);
188+ case Type::Double:
189+ return isNegativeInf (-m_doubleValue);
190+ case Type::String:
191+ return m_stringValue == " -Infinity" ;
192+ default :
193+ return false ;
194+ }
195+ }
147196
148197 /* ! Returns true if the value is NaN (Not a Number). */
149- bool isNaN () const { return m_type == Type::NaN; }
198+ bool isNaN () const
199+ {
200+ switch (m_type) {
201+ case Type::NaN:
202+ return true ;
203+ case Type::Double:
204+ assert (!std::isnan (m_doubleValue));
205+ return std::isnan (m_doubleValue);
206+ case Type::String:
207+ return m_stringValue == " NaN" ;
208+ default :
209+ return false ;
210+ }
211+ }
150212
151213 /* ! Returns true if the value is a number. */
152214 bool isNumber () const { return m_type == Type::Integer || m_type == Type::Double; }
153215
216+ /* ! Returns true if the value is a number or can be converted to a number. */
217+ bool isValidNumber () const
218+ {
219+ if (isNaN ())
220+ return false ;
221+
222+ if (isInfinity () || isNegativeInfinity ())
223+ return true ;
224+
225+ assert (m_type != Type::Infinity && m_type != Type::NegativeInfinity);
226+
227+ switch (m_type) {
228+ case Type::Integer:
229+ case Type::Double:
230+ case Type::Bool:
231+ return true ;
232+ case Type::String:
233+ return m_stringValue.empty () || checkString (m_stringValue) > 0 ;
234+ default :
235+ return false ;
236+ }
237+ }
238+
154239 /* ! Returns true if the value is a boolean. */
155240 bool isBool () const { return m_type == Type::Bool; }
156241
@@ -163,16 +248,9 @@ class LIBSCRATCHCPP_EXPORT Value
163248 /* ! Returns the long representation of the value. */
164249 long toLong () const
165250 {
166- if (static_cast <int >(m_type) < 0 ) {
167- switch (m_type) {
168- case Type::Infinity:
169- return std::numeric_limits<long >::infinity ();
170- case Type::NegativeInfinity:
171- return -std::numeric_limits<long >::infinity ();
172- default :
173- return 0 ;
174- }
175- } else {
251+ if (static_cast <int >(m_type) < 0 )
252+ return 0 ;
253+ else {
176254 switch (m_type) {
177255 case Type::Integer:
178256 return m_intValue;
@@ -376,8 +454,17 @@ class LIBSCRATCHCPP_EXPORT Value
376454 if (m_type == Type::String)
377455 m_stringValue.~basic_string ();
378456
379- m_type = Type::Double;
380- m_doubleValue = floatToDouble (v);
457+ if (isInf (v))
458+ m_type = Type::Infinity;
459+ else if (isNegativeInf (v))
460+ m_type = Type::NegativeInfinity;
461+ else if (std::isnan (v))
462+ m_type = Type::NaN;
463+ else {
464+ m_type = Type::Double;
465+ m_doubleValue = floatToDouble (v);
466+ }
467+
381468 return *this ;
382469 }
383470
@@ -386,8 +473,16 @@ class LIBSCRATCHCPP_EXPORT Value
386473 if (m_type == Type::String)
387474 m_stringValue.~basic_string ();
388475
389- m_type = Type::Double;
390- m_doubleValue = v;
476+ if (isInf (v))
477+ m_type = Type::Infinity;
478+ else if (isNegativeInf (v))
479+ m_type = Type::NegativeInfinity;
480+ else if (std::isnan (v))
481+ m_type = Type::NaN;
482+ else {
483+ m_type = Type::Double;
484+ m_doubleValue = v;
485+ }
391486 return *this ;
392487 }
393488
@@ -423,30 +518,33 @@ class LIBSCRATCHCPP_EXPORT Value
423518
424519 const Value &operator =(const std::string &v)
425520 {
426- if (m_type == Type::String)
427- m_stringValue = v;
428- else {
429- new (&m_stringValue) std::string (v);
430- m_type = Type::String;
431- }
521+ if (v == " Infinity" ) {
522+ if (m_type == Type::String)
523+ m_stringValue.~basic_string ();
432524
433- initString (v);
434- return *this ;
435- }
525+ m_type = Type::Infinity;
526+ } else if (v == " -Infinity" ) {
527+ if (m_type == Type::String)
528+ m_stringValue.~basic_string ();
436529
437- const Value &operator =(const char *v)
438- {
439- if (m_type == Type::String)
530+ m_type = Type::NegativeInfinity;
531+ } else if (v == " NaN" ) {
532+ if (m_type == Type::String)
533+ m_stringValue.~basic_string ();
534+
535+ m_type = Type::NaN;
536+ } else if (m_type == Type::String)
440537 m_stringValue = v;
441538 else {
442539 new (&m_stringValue) std::string (v);
443540 m_type = Type::String;
444541 }
445542
446- initString (v);
447543 return *this ;
448544 }
449545
546+ const Value &operator =(const char *v) { return (*this = std::string (v)); }
547+
450548 const Value &operator =(const Value &v)
451549 {
452550 switch (v.m_type ) {
@@ -496,59 +594,22 @@ class LIBSCRATCHCPP_EXPORT Value
496594 std::string m_stringValue;
497595 };
498596
499- // -1 - error
500597 // 0 - is string
501598 // 1 - is long
502599 // 2 - is double
503- static int checkString (const std::string &str, long *longValue, double *doubleValue )
600+ static int checkString (const std::string &str)
504601 {
505- if (!longValue || !doubleValue)
506- return -1 ;
507-
508602 bool ok;
509603
510604 if ((str.find_first_of (' .' ) == std::string::npos) && (str.find_first_of (' e' ) == std::string::npos) && (str.find_first_of (' E' ) == std::string::npos)) {
511- *longValue = stringToLong (str, &ok);
605+ stringToLong (str, &ok);
512606 return ok ? 1 : 0 ;
513607 } else {
514- *doubleValue = stringToDouble (str, &ok);
608+ stringToDouble (str, &ok);
515609 return ok ? 2 : 0 ;
516610 }
517611 }
518612
519- void initString (const std::string &str)
520- {
521- if (str.empty ())
522- return ;
523- else if (str == " Infinity" ) {
524- m_type = Type::Infinity;
525- return ;
526- } else if (str == " -Infinity" ) {
527- m_type = Type::NegativeInfinity;
528- return ;
529- } else if (str == " NaN" ) {
530- m_type = Type::NaN;
531- return ;
532- }
533-
534- long l;
535- double d;
536- int type = checkString (str, &l, &d);
537-
538- switch (type) {
539- case 1 :
540- *this = l;
541- m_type = Type::Integer;
542- break ;
543- case 2 :
544- *this = d;
545- m_type = Type::Double;
546- break ;
547- default :
548- break ;
549- }
550- }
551-
552613 double getNumber (bool *ok = nullptr ) const
553614 {
554615 // Equivalent to JavaScript Number(), *ok == false means NaN
@@ -589,8 +650,6 @@ class LIBSCRATCHCPP_EXPORT Value
589650 }
590651 }
591652
592- void initString (const char *str) { initString (std::string (str)); }
593-
594653 Type m_type;
595654
596655 friend bool operator ==(const Value &v1, const Value &v2)
@@ -922,7 +981,7 @@ class LIBSCRATCHCPP_EXPORT Value
922981 }
923982 }
924983
925- static const std::string digits = " 0123456789+-" ;
984+ static const std::string digits = " 0123456789.eE +-" ;
926985
927986 for (char c : s) {
928987 if (digits.find (c) == std::string::npos) {
@@ -944,7 +1003,17 @@ class LIBSCRATCHCPP_EXPORT Value
9441003 static std::string doubleToString (double v)
9451004 {
9461005 std::stringstream stream;
947- stream << std::setprecision (std::max (16u , digitCount (v))) << v;
1006+
1007+ if (v != 0 ) {
1008+ const int exponent = std::log10 (std::abs (v));
1009+
1010+ if (exponent > 20 )
1011+ stream << std::scientific << std::setprecision (digitCount (v / std::pow (10 , exponent + 1 )) - 1 ) << v;
1012+ else
1013+ stream << std::setprecision (std::max (16u , digitCount (v))) << v;
1014+ } else
1015+ stream << std::setprecision (std::max (16u , digitCount (v))) << v;
1016+
9481017 std::string s = stream.str ();
9491018 std::size_t index;
9501019
@@ -989,6 +1058,18 @@ class LIBSCRATCHCPP_EXPORT Value
9891058
9901059 return i + j;
9911060 }
1061+
1062+ template <typename T>
1063+ static bool isInf (T v)
1064+ {
1065+ return v > 0 && std::isinf (v);
1066+ }
1067+
1068+ template <typename T>
1069+ static bool isNegativeInf (T v)
1070+ {
1071+ return v < 0 && std::isinf (v);
1072+ }
9921073};
9931074
9941075} // namespace libscratchcpp
0 commit comments