string->number(3scm)

πŸ“¦ version 0-unstable-2024-02, in nixpkgs unstable

πŸ’– string->number(3scm) πŸ’–

okay bestie, so this is page string->number(3scm) from the Scheme Programmer's Manual, and it's literally the glow-up guide for turning a lil string into a whole number. buckle up πŸŽ€


✨ NAME ✨

string->number β€” convert a string to a number. that's it, that's the whole vibe πŸ’…


πŸ“š LIBRARY πŸ“š

ok so depending on which flavor of Scheme you're living in, you import one of these:

(import (rnrs))                     ;R6RS
(import (rnrs base))                ;R6RS
(import (scheme r5rs))              ;R7RS
(import (scheme base))              ;R7RS

pick your fighter, they all slay 🫢


🌸 SYNOPSIS 🌸

(string->number string)
(string->number string radix)

two forms, one iconic procedure πŸ‘‘


πŸ’« DESCRIPTION πŸ’«

so basically it returns a number object with maximally precise representation expressed by the given string. fancy way of saying: it reads the string, gives you the most precise number it can. periodt ✨

radix has to be an exact integer object, and it has to be one of 2, 8, 10, or 16. literally only those four, no negotiating πŸ™…β€β™€οΈ

if you do pass radix, it's the default radix β€” but heads up, it may be overridden by an explicit radix prefix written right there in the string itself, like "#o177". sneaky but valid πŸ’…

if you don't pass radix? the default radix is 10. classic.

now the important part, bestie: if string is not a syntactically valid notation for a number, or it would result in a number that the implementation cannot represent, then string->number returns #f. and no cap β€” an error is never signaled due to the content of string. it just politely hands you #f and minds its business 😌


🧚 IMPLEMENTATION NOTES 🧚

Chez Scheme

the string->number procedure in the (chezscheme) library accepts a radix between 2 and 36. yeah, it's built different. lowkey flexing on the R6RS kids πŸ’€


πŸŽ€ RETURN VALUES πŸŽ€

returns a single value which is a number object or #f. one value. that's the whole return party πŸŽ‰


🌈 EXAMPLES 🌈

the basics, and they ate:

(string->number "100")     =>  100
(string->number "100" 16)  =>  256
(string->number "1e2")     =>  100.0

and now the R6RS ones β€” so here's the tea: an implementation may return #f instead of infinities or nans if they are not supported. and importantly, it does not, like it otherwise would, raise the conditions from make-no-infinities-violation(3scm) or make-no-nans-violation(3scm).

(string->number "0/0")     =>  #f
(string->number "+inf.0")  =>  +inf.0
(string->number "-inf.0")  =>  -inf.0
(string->number "+nan.0")  =>  +nan.0

see how "0/0" is out here getting #f while the infinities are thriving? yeah. that's the whole point of that note 😭


πŸ’œ RATIONALE πŸ’œ

why does this exist? because it gives you access to the same number parser that is used in read(3scm) β€” or at least a functionally equivalent parser. consistency queen πŸ‘‘

R7RS explains it like this, and honestly it's giving main character energy:

The rules used by a particular implementation for string->number must also be applied to read and to the routine that reads programs, in order to maintain consistency between internal numeric processing, I/O, and the processing of programs. As a consequence, the R5RS permission to return #f when string has an explicit radix prefix has been withdrawn.

so yeah β€” that old permission? withdrawn. gone. it's not coming back 🫑


πŸ¦‹ COMPATIBILITY πŸ¦‹

you can look at this procedure two ways and get two whole different readings, ngl.

way one: if you see it as just "a door into the implementation's number parser," then it essentially works the same everywhere. universal vibe ✨

way two: if you look at which inputs give which results, then there can be large differences. like, big big. 😬

R6RS implementations are basically the same, except they may lack support for infinities and nans. R7RS implementations have much more leeway, and here's the official rundown:

The domain of string->number may be restricted by implementations in the following ways. If all numbers supported by an implementation are real, then string->number is permitted to return #f whenever string uses the polar or rectangular notations for complex numbers. If all numbers are integers, then string->number may return #f whenever the fractional notation is used. If all numbers are exact, then string->number may return #f whenever an exponent marker or explicit exactness prefix is used. If all inexact numbers are integers, then string->number may return #f whenever a decimal point is used.

so if your implementation is out here with a limited domain, it's allowed to hand you #f for all those fancy notations. not a bug, just the rules πŸ’…


⭐ ERRORS ⭐

this procedure can raise exceptions with the following condition types:

  • &assertion (R6RS) β€” the wrong number of arguments was passed, string was not a string object, or radix was passed but was not 2, 8, 10, or 16. all three of those will get you snapped at πŸ“’
  • R7RS β€” the assertions described above are errors. implementations may signal an error, extend the procedure's domain of definition to include such arguments, or fail catastrophically. yeah. catastrophically. that's the actual word. no cap πŸ’€

πŸ‘€ SEE ALSO πŸ‘€

  • number->string(3scm) β€” the reverse queen, go say hi 🫢

πŸ† STANDARDS πŸ†

R4RS, IEEE Scheme, R5RS, R6RS, R7RS

five for five, she's certified ✨


🌸 HISTORY 🌸

first appeared in R2RS, where it took three arguments: string, exactness, and radix. and the latter two arguments were symbols. wild times, truly ancestor behavior πŸ™ˆ

the modern two-argument variant first appeared in R4RS. glow-up complete πŸ’…


🫢 AUTHORS 🫢

this page is part of the scheme-manpages project. it includes materials from the RnRS documents. more information can be found at https://github.com/schemedoc/manpages/ β€” go give them a follow, they're icons πŸ‘‘


πŸ’€ BUGS πŸ’€

ok bestie this is the serious one, pay attention:

Applications should not pass untrusted input to this procedure. It is easy to make it consume large amounts of memory and CPU time.

here's the receipts, literally the actual example from the page:

(time (bitwise-length (string->number "#e1e1000000")))
    no collections
    2.256397608s elapsed cpu time
    2.256396107s elapsed real time
    1392400 bytes allocated
3321929

so the number itself consumes several megabytes of memory and the conversion takes much longer to run than would be expected for such a short input. 😭😭😭

and because the universe has no mercy β€” adding a few more zeroes makes it an easy DoS. literally a few zeroes. that's all it takes. please guard your inputs, bestie, i'm begging πŸ™


that's the whole page, you're officially educated πŸŽ“πŸ’– this gem dropped in the scheme-manpages source, dated 2022-04-10, and was served to you fresh as string->number(3scm). go forth and parse responsibly βœ¨πŸ¦‹πŸŒΈ

πŸ“¦ where this page comes from