はじめに
初投稿です。めちゃくちゃ分かりづらい記事になっている気がします。
Scratchというサイトとプログラミング言語があります。Scratchでは「クラウド変数」という機能が追加されています。クラウド変数はサーバーにデータが保存され、Scratcherであれば誰でも変更できます。この機能を応用すれば、オンラインゲームなども作成できます。
今回はクラウド変数について詳しく見ていきます。
クラウド変数の制限
とても便利なクラウド変数ですが、いくつかの制限があります。
- 基本的にNew Scratcherだと使えない
- 一つのプロジェクトに10個しか作れない
- 基本的に0.1秒のレート制限がある
- クラウド変数に保存できる値には安全性確保やサーバーへの負荷を弱くするのための制限がある
今回は「クラウド変数に保存できる値」について考え、その容量の限界を調べてみます。クラウド変数の制限は詳しく明らかにされていませんが、有名なものとして、「数値しか保存できない」や「256文字しか保存できない」があります。
その他の制限としては、
- +がついているものは保存不可能
- -は最初の文字でしか置けない
- 小数点はどこにでも置けるが、1つのみ
- 空白(スペースではない)でも保存できる
- -や.単体では保存不可能
- NaNやInfinityは保存不可能
- 指数表記も内部的にNumber型にすれば保存可能
といった条件があります。
容量を計算する
では、容量を計算していきます。
まずは内部的にString型として保存される情報についてです。
正の数
正の小数点が含まれない文字列としての数については、
$$\sum_{k=0}^{256}10^k$$
で計算できます。k=0のときは""(何も保存しない)が保存できるので問題ありません。クラウド変数には"0005"のような値も保存できるのでこれで大丈夫です。
次に正の小数点が含まれる文字列としての数についてです。
$$\sum_{k=1}^{255}10^k(k+1)$$
で問題ないでしょう。k=0の場合は"."になるのですが、これは前述した通り保存できません。シグマの上を255に設定している理由は、先に"."を配置しているものだと考えればわかりやすいでしょう。
負の数
負の小数点が含まれない文字列としての数は、
$$\sum_{k=1}^{255}10^k$$
となります。
小数点が含まれている場合は、
$$\sum_{k=1}^{254}10^k(k+1)$$
です。これでk=2と設定しないのは、"-2."のような値も保存できるからです。ちなみに"-."は保存されません。
Number型の場合
Number型の1000もString型の"1000"もクラウド変数のサーバー内部では同じようにStringとして保存されます。Number型はすぐにStringに変換されます。この変換はクライアント側で行われていますが、それをすっ飛ばしてNumber型のままデータを送っても上手くいかないため、Stringで保存されるように設計されています。
1e+20はScratchやJavaScriptの仕様では100000000000000000000となってしまいます。これだとStringで保存されるのと変わりませんよね?指数表記で保存されるものを調べたいということです。
ここで仕様書を使います。読めば分かりますが、$$x<10^{-6},x\leq10^{21}$$のどちらかが成り立つ x は指数表記になります。
ということで、
$$(0, 2^{-20}]$$
$$(2^{-20}, 10^{-6}]$$
$$[10^{21},2^{70})$$
$$[2^{70},\infty)$$
の4つの区間について倍精度浮動小数点でそれぞれ考えていきます。
$$(0, 2^{-20}]$$について、$$2^{-20}=2^{1003-1023}$$のため、
$$1003*2^{52}$$で個数を求められますね。
次に
$$(2^{-20}, 10^{-6}]$$ですが、浮動小数点数内部表現シミュレーターによると$$10^{-6}=3eb0c6f7a0b5ed8d_{(16)}*2^{-20}$$
が成り立つようです。よって、
$$3eb0c6f7a0b5ed8d_{(16)}$$
がその値になります。
では、$$[10^{21},2^{70})$$についてです。
$$10^{21}=b1ae4d6e2ef50_{(16)}*2^{69}$$
よって、答えは
$$2^{52}-b1ae4d6e2ef50_{(16)}$$
でいいです。では、さいごの$$[2^{70},\infty)$$やります。
[1093,2047)なので、答えは
$$954*2^{52}$$
ですね。負の数は同じものの和でできます。
では、JavaScriptのBigIntを用いて足し算していきます!
let tmp = 0n;
for(let i = 0n;i<256n;i++){
tmp += 10n**i
}
for(let i = 1n;i<255n;i++){
tmp += 10n**i*(i+1n)
}
for(let i = 1n;i<255n;i++){
tmp += 10n**i
}
for(let i = 1n;i<254n;i++){
tmp += 10n**i*(i+1n)
}
tmp +=1003n*2n**52n
//3eb0c6f7a0b5ed8d(16)=4517329193108106637(10)
tmp += 4517329193108106637n
//b1ae4d6e2ef50=3125794903879504
tmp += 2n**52n-3125794903879504n
tmp += 954n*2n**52n
console.log(tmp.toString())
電卓としてつかったので適当ですが、こんな感じでやると「32364197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530864197530877529782332793189162」という値になります。これが何bitかというと、底が2の対数にいれるため、852.1079910239091bitとなります。
指数表記を含めると13332251468595658301通り増えました。めちゃくちゃ少ない……。
さいごに
指数表記とか面倒なことに囚われてもほとんど意味がありませんよ!諦めて文字列連結を頑張りましょう!