Page 1 of 1
StrD() Inconsistency
Posted: Fri Sep 03, 2021 10:09 pm
by swhite
Hi
I think the StrD() function in inconsistent as shown below. StrD(1.0,0) should show 1 but StrD(1.0) should show 1.0. This becomes an issue when you are working with JSON. If you happen to have price of 1.00 in the database and you use StrD to load the value as JSON then you get "price":1. Then when you parse the JSON you have no way to tell if this value should be handled as an integer or a double because the decimal point has been removed.
I think StrD() should always return with the same number of decimals places as the value contains when the number of decimal places is not specified.
Code: Select all
Debug StrD(1.2) ;shows 1.2
Debug StrD(1.0) ;Show 1 expected 1.0
Re: StrD() Inconsistency
Posted: Sat Sep 04, 2021 12:00 am
by STARGĂ…TE
swhite wrote: Fri Sep 03, 2021 10:09 pm
I think StrD() should always return with the same number of decimals places as the value contains when the number of decimal places is not specified.
There in no
"the same number of decimals places as the value contains" in doubles. 1.400 is the same as 1.4
All double numbers "contain" roughly 16 significant decimal digits and when you return all of them you end up some times with:
Code: Select all
Debug StrD(1.4, 16) ; returns 1.3999999999999999
Debug StrD(1.1, 16) ; returns 1.1000000000000001
Not a good idea.
Futher StrD() is not useful for large or small numbers:
Code: Select all
Debug StrD(1.602e-19) ; returns 0
Debug StrD(5.972e24) ; returns 5971999999999999300000000
Both is not good while storing them.
If you want to store doubles you can (or have to) use StrSci()
Code: Select all
Procedure.s StrSci(Double.d, Digits.i=12, Symbol.s="e") ; By default only 12 significant decimal digits are used.
Protected Log10.d, String.s
If IsNAN(Double) Or IsInfinity(Double)
ProcedureReturn StrD(Double)
ElseIf Double = 0.0
ProcedureReturn "0.0"
Else
Log10 = Round(Log10(Abs(Double)), #PB_Round_Down)
If Log10 < -300
Double * 1e100 * Pow(10, -Log10-100)
Else
Double * Pow(10, -Log10)
EndIf
String = RTrim(StrD(Double, Digits), "0")
If Right(String, 1) = "." : String + "0" : EndIf
If Log10 <> 0
ProcedureReturn String + Symbol + Str(Log10)
Else
ProcedureReturn String
EndIf
EndIf
EndProcedure
Debug StrSci(1.4) ; returns 1.3
Debug StrSci(1.1) ; returns 1.1
Debug StrSci(1.0) ; returns 1.0
Debug StrSci(0.2) ; returns 2.0e-1
Debug StrSci(1.602e-19) ; returns 1.602e-19
Debug StrSci(5.972e24) ; returns 5.972e24
1.4
1.1
1.0
2.0e-1
1.602e-19
5.972e24
Re: StrD() Inconsistency
Posted: Sat Sep 04, 2021 1:49 pm
by swhite
Hi
At the least StrD() should not return a value without at least 1 decimal place. So if the value appears to be a whole number then simply return it with a decimal place followed by zero (2.0 not 2 etc.).
Simon
Re: StrD() Inconsistency
Posted: Sat Sep 04, 2021 2:12 pm
by #NULL
StrD(1, 1) gives 1.0 so what's the problem? if you want one decimal then specify it.
And the documentation says
NbDecimals:
... If omitted, it will be set to 10 decimal places, with removing the trailing zeros.
And apart from that, you shouldn't use a type based on the number format. You should use a type based on what that property represents in that API.
Re: StrD() Inconsistency
Posted: Sat Sep 04, 2021 4:58 pm
by Jeff8888
Thanks stargate for your procedure StringSci(). I immediately saved it for future reference.
Re: StrD() Inconsistency
Posted: Mon Sep 06, 2021 2:21 pm
by swhite
Hi
The simple problem is that I am reading data from a table where I do not know the decimal places. If I knew the decimal places in the first place I would not have made this request. If you want to handle data in a generic way you can not be hard coding assumptions in your code.
#NULL wrote: Sat Sep 04, 2021 2:12 pm
StrD(1, 1) gives 1.0 so what's the problem? if you want one decimal then specify it.
And the documentation says
NbDecimals:
... If omitted, it will be set to 10 decimal places, with removing the trailing zeros.
And apart from that, you shouldn't use a type based on the number format. You should use a type based on what that property represents in that API.
Re: StrD() Inconsistency
Posted: Wed Sep 08, 2021 7:52 pm
by kenmo
Hi, I think there are two separate concerns colliding here:
1. StrD()
Remember, whether you write 1.4 or 1.400, it results in the exact same floating point value, so StrD() has no idea how many decimal places you originally intended.
Code: Select all
a.d = 1.4
b.d = 1.400
Debug Bin(PeekQ(@a), #PB_Quad)
Debug Bin(PeekQ(@b), #PB_Quad)
Debug StrD(a)
Debug StrD(b)
StrD() is doing what it claims, removing trailing zeroes, StrD(1.0) --> "1"
That being said... I agree it should always show AT LEAST the 1 decimal places (unless you specify zero places), StrD(1.0) --> "1.0"
because in PB and other languages the ".0" indicates it's a float/double, not integer!
But I don't expect the behavior of a basic existing PB function to change after 20 years
2. JSON
The JSON format does not specify whether a "number" is integer or float/double... see Wikipedia
https://en.wikipedia.org/wiki/JSON
The format makes no distinction between integer and floating-point.
Numbers in JSON are agnostic with regard to their representation within programming languages. While this allows for numbers of arbitrary precision to be serialized, it may lead to portability issues. For example, since no differentiation is made between integer and floating-point values, some implementations may treat 42, 42.0, and 4.2E+1 as the same number, while others may not. The JSON standard makes no requirements regarding implementation details such as overflow, underflow, loss of precision, rounding, or signed zeros, but it does recommend to expect no more than IEEE 754 binary64 precision for "good interoperability".
That's just the way it is. And because the PB JSON lib does give access the "raw original text" of the stored number, you don't know if it contained a decimal point. The simplest estimate is to check if
Int(MyDouble) = MyDouble
Re: StrD() Inconsistency
Posted: Thu Sep 09, 2021 7:11 pm
by swhite
Hi
The problem with Int(MyDouble) = MyDouble is that you cannot distinguish 13 from 13.0 so you will get an error when trying to assign the value to a memory variable whose type does not match the value being assigned. If the JSON library allowed you to retrieve any value as string this problem could be solved easily.
Simon
Re: StrD() Inconsistency
Posted: Fri Sep 10, 2021 4:34 am
by kenmo
Right, Int(MyDouble) = MyDouble is just a guess of whether it "seems" to be an integer or not.
What I don't understand is your real usage: Do you control the JSON being parsed?
If you know the meanings of the numbers being parsed, you can assign them to integers or doubles, whatever makes sense.
Remember according to JSON spec it's just a generic "number".
Or, if you generate this JSON, you could include "isInteger":true or "isDouble":false or similar.
But if it's generated by a third party source, and you don't know whether integer or double is appropriate, then I see your problem.
If you really, really need a workaround for now, check out my JSON parser from ~2014, shortly before PB added its JSON functions.
https://github.com/kenmo-pb/includes/bl ... /OJSON.pbi
It DOES give you access to the raw text of a number, such as "13.000".
(It also does something the PB functions don't: preserves the order within JSON objects, which is why I keep this around as "OJSON" - "Ordered JSON". Sometimes that's needed.)
Code: Select all
Text.s = "{ 'myString' : 'Testing...', 'myNumber' : 13.000 }"
Text = ReplaceString(Text, "'", #DQUOTE$)
*OJ = CatchOJSON(@Text, StringByteLength(Text))
If (*OJ)
*Obj = MainOJSONNode(*OJ)
Debug GetOJSONNodeText( NamedOJSONNode(*OJ, "myString") ) ; Testing...
Debug GetOJSONNodeValue( NamedOJSONNode(*OJ, "myNumber") ) ; 13.0
Debug GetOJSONNodeText( NamedOJSONNode(*OJ, "myNumber") ) ; 13.000
FreeOJSON(*OJ)
EndIf
Re: StrD() Inconsistency
Posted: Fri Sep 10, 2021 2:21 pm
by swhite
Hi
The problem is that I do not control the JSON. It is data extracted from database tables and sent via JSON. So it is important that I can at least tell if a number is an integer or not. To solve this problem I simply built my own simply JSON parser and so if the number contains a decimal I convert it to double otherwise I treat it as an integer and that works for my use case.
Simon
Re: StrD() Inconsistency
Posted: Mon Sep 13, 2021 3:27 am
by kenmo
Glad you have a working solution

Maybe your feature request will simplify this in the future.