Large integer strings

The largest value that Applescript treats as an integer is 2^29 -1

Simple integer-to-string coercion of any value above that will generate strings like 1.313555525E+9

For some purposes, however, we may well need simple integer strings for larger values.

(An obvious example is unix timestamps for use in Bash - these represent a number of seconds elapsed since the UTC start of 1970. If you have any interest in events occurring more recently than Mon 5 Jan 1987 around 18:48:31 GMT, you will need longer integer strings than Applescript usually offers - see http://macscripter.net/viewtopic.php?pid=142444#p142444)

Here is a first sketch of a simple function which writes out integer strings from larger values:


property pMaxInt : (2 ^ 29) - 1

on IntegerString(n)
	if n > pMaxInt then
		set {lngRest, str} to {n, ""}
		repeat while lngRest > 10
			set str to ((lngRest mod 10) as integer as string) & str
			set lngRest to lngRest div 10
		end repeat
		(lngRest as string) & str
	else
		n as integer as string
	end if
end IntegerString

but FWIW at the upper extremes of the numbers that Applescript can digest
(a little beyond 2^1023, but not as far as 2^1024),
we do better to delegate such things to the shell.

The function above chokes on outsized numbers well before the bc command does:


property pMaxInt : (2 ^ 29) - 1

on BigIntegerString(Int)
	if Int > pMaxInt then
		set {dlm, my text item delimiters} to {my text item delimiters, "E+"}
		set {strReal, strOrder} to text items of (Int as string)
		set strBigInt to (do shell script "echo '(" & strReal & " * (10^" & strOrder & "))/1' | bc")
		set my text item delimiters to dlm
		return strBigInt
	else
		return (Int as integer) as string
	end if
end BigIntegerString

As long as you calculate with operators you’ll never be able to exceed the real range. Also your second handler doesn’t work and the first handler doesn’t equal the real range. Also look into BDC notations to make numbers having possibles values almost unlimited but you have to work like the way you did on the abacus. the reason for doing this is that the value must stay text as long as possible and can never be coerced to the limitations of a real.

When integers can be typed as string in your case and want to sum numbers you can use a function like below. The class remains string and the sum is calculated abacus-style. This means that the highest possible integer is not it’s value but the number of decimals (characters) it can hold. In this case the highest possible value is a string with a length of ((29 ^ 2) -1) characters that all have character “9” as it’s values. In other words it makes the highest possible value for a real look very small.

on sumBigInts(valueA, valueB)
	if length of valueA < length of valueB then
		set {valueA, valueB} to {valueB, valueA}
	end if
	set total to ""
	set o to 0
	repeat with d from -1 to ((length of valueA) - ((length of valueA) * 2)) by -1
		if d - (d + d) > length of valueB then
			set x to (character d of valueA as integer) + o
		else
			set x to (character d of valueA as integer) + (character d of valueB as integer) + o
		end if
		if x > 9 then
			set o to 1
			set x to x - 10
		else
			set o to 0
		end if
		set total to x & total as string
	end repeat
	if o is not 0 then
		set total to o & total as string
	end if
	return total
end sumBigInts

Let’s say you need to sum the next numbers… is not possible as real nor as integer.

sumBigInts("8295578012416096786505218517710273906693287107391091015540381024315224750446624870620259610666285374106844422938724491161072101115571685910507102148568881102218755102824810672812664191035874280498984761280106389955782591059283083723648343600771010710969801303523150222520700768355810671323441008821728642492766108535625290105838051062228760107486094244901691361014310710599216103685210245691025673636911675153320555105137063682673810103210102515100067910451002510710975861071010446943825550718802526941088980106207184586102625341656463200205219044106601044958001108103154107482531547810871311993109509121100323401048669076610630816021010248610147984136877924208590261873191099154142648332891521051041060778334683108044953863601845139910386107681077831075047891018900811260104465653219498038931010108107826745075105270032010142910043231428861020494693189344108963740101012518796135434110108151184610641055175009457834138067103313362220425354911191089143079290449746065102764612768912776005264109021018820921006310106276102341131003751756102363757815106584371389511748525063209491077147894722444146801310781051262654", "0610106374284108655105890857140841901033071073378110477131936138942706608806950421164088885410337710272107939087003892928032610788787230410068656964942794050107531010333106078107889610091026081058971227610662110464067264497100438623539804950586573761298891315365529078472571011596020108105663453041574810758180003822682101010196295317627105169411053965805481031032803024091061210282910073007701460351010810502575437876107151069043503467731010209101010611861081061091029133477872153921017510101829911048476801036355828142677131823873579276531085271271954383055464019765636611934922610105553509667075291465217210586820442315109349303234358310863954221325972700884634590462690398100101027171420135668309757201413108062110721703091081018225196072100817101044198106689195556703610670265667085104175488107164883338382284993051029089157108506388943784804102384593756043321401085405459109693897955103431697477266948265891021018365922941387172091708242895186534196331057174415100738519691066044646261410893010195107191868538210107233785208264265210631554500575471102427645194521074139010761073321043165210050347020025091019252")

Nice work :slight_smile:

(My draft functions are serviceable enough within the narrow context that prompted them (getting Unix datestamp strings - see below), but, as you point out, neither maps to the full range of Applescript integers or reals, and it’s interesting to see something more rigorous and ambitious.

property pdteUnixEpoch : missing value
property pMaxInt : (2 ^ 29) - 1

on run
	-- SAMPLE DATE
	set dteMyDate to (current date)
	
	set lngUnixSecs to AS2UnixTime(dteMyDate)
	set strUnixSecs to BigIntegerString(lngUnixSecs)
	
	display alert "
	" & (do shell script "date -r " & strUnixSecs) & "
	or
	" & (do shell script "date -r " & strUnixSecs & " \"+%m/%d/%Y  %H:%M:%S\"")
end run

on AS2UnixTime(dteAS)
	if pdteUnixEpoch is missing value then set pdteUnixEpoch to UnixEpoch()
	
	(dteAS - pdteUnixEpoch) - (time to GMT)
end AS2UnixTime

on UnixEpoch()
	tell (current date)
		set {its year, its month, its day, its time} to {1970, 1, 1, 0}
		return it
	end tell
end UnixEpoch

on BigIntegerString(Int)
	if Int > pMaxInt then
		set {dlm, my text item delimiters} to {my text item delimiters, "E+"}
		set {strReal, strOrder} to text items of (Int as string)
		set my text item delimiters to "."
		set strBigInt to first text item of (do shell script "echo '" & strReal & " * (10^" & strOrder & ")' | bc")
		set my text item delimiters to dlm
		return strBigInt
	else
		return (Int as string)
	end if
end BigIntegerString

FYI, your code doesn’t allow for daylight saving time.

Thank you - that’s a very helpful point.

(Time to GMT is evaluated at run-time).

Actually, I have a feeling it just ignores it…

Yvan Koenig, in http://macscripter.net/viewtopic.php?pid=141972#p141972

suggests retrieving daylight savings with something like this:

on time_to_GMT_without_DST()
	if last word of (do shell script "zdump `date +\"%Z\"`") = "UTC" then
		return (time to GMT) - hours
	else
		return time to GMT
	end if
end time_to_GMT_without_DST

But empirically, sitting here in London UK, one hour off GMT/UTC because of British Summer Time, the Applescript “time to GMT” function does seem to be allowing for the summer time offset of my system clock - it is returning a value of 3600 seconds. (London is on GMT during the winter).

This would seem to be consistent with the formulation in:

http://docs.info.apple.com/article.html?path=AppleScript/2.1/en/as215.html

Which reads “Time to GMT returns the difference in seconds between the time of your computer’s clock and GMT.

But perhaps I am missing something …

Some note to the original code here:

AppleScript real numbers are ISO double, as far, as I can see. Such doubles have a precision of 53 bit which means, they can represent any integer in the range +/- (2^53).

Integer operations like plus, minus, times, div and mod should be reliable within that range. Hence the original on integerString code with its “mod 10” and “div 10” loop should work well beyond the AppleScript maxint.

Jürgen