StrD() Inconsistency

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
swhite
Enthusiast
Enthusiast
Posts: 727
Joined: Thu May 21, 2009 6:56 pm

StrD() Inconsistency

Post 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
Simon White
dCipher Computing
User avatar
STARGÅTE
Addict
Addict
Posts: 2084
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: StrD() Inconsistency

Post 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
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
swhite
Enthusiast
Enthusiast
Posts: 727
Joined: Thu May 21, 2009 6:56 pm

Re: StrD() Inconsistency

Post 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
Simon White
dCipher Computing
#NULL
Addict
Addict
Posts: 1440
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Re: StrD() Inconsistency

Post 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.
Jeff8888
User
User
Posts: 38
Joined: Fri Jan 31, 2020 6:48 pm

Re: StrD() Inconsistency

Post by Jeff8888 »

Thanks stargate for your procedure StringSci(). I immediately saved it for future reference.
swhite
Enthusiast
Enthusiast
Posts: 727
Joined: Thu May 21, 2009 6:56 pm

Re: StrD() Inconsistency

Post 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.
Simon White
dCipher Computing
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Re: StrD() Inconsistency

Post 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!

Code: Select all

Debug 55 / 2
Debug 55 / 2.0
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
swhite
Enthusiast
Enthusiast
Posts: 727
Joined: Thu May 21, 2009 6:56 pm

Re: StrD() Inconsistency

Post 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
Simon White
dCipher Computing
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Re: StrD() Inconsistency

Post 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
swhite
Enthusiast
Enthusiast
Posts: 727
Joined: Thu May 21, 2009 6:56 pm

Re: StrD() Inconsistency

Post 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
Simon White
dCipher Computing
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Re: StrD() Inconsistency

Post by kenmo »

Glad you have a working solution :) Maybe your feature request will simplify this in the future.
Post Reply