紀元前の西暦と修正ユリウス日の相互変換をしたい!


 あるいは、ガウス記号ってゆうか床関数ってゆうかについて知った話し。
 あるいはまた、負数の入った剰余はムズイとゆう話し。

発端

 そもそもは、なでしこ1にはあった「修正ユリウス日取得」が、なでしこ3には無かったので、取得したかったのでした。
 また、なでしこ1の修正ユリウス日取得は、西暦は全てグレゴリオ暦の遡りとなっているのですが、実際に即して1582/10/15以降はグレゴリオ暦、それ以前はユリウス暦として計算したい。
 そしてもちろん修正ユリウス日から、グレゴリオ暦やユリウス暦に戻したい(なぜかなでしこではできない)
 ・・・とゆうわけで去年一度やってみて、紀元後についてはいちおうできるようになっていたと思うんですが、ワタシの知恵が不足すぎて紀元前の取得がちゃんと出来ていなかったのを一念発起して出来るようにしようと思い立ったのです!

修正ユリウス日の取得

 検算はここで行います。

 計算式は例によってうぃきぺでぃあ先生に教わります。

西暦と修正ユリウス日との相互換算

 この式の、大括弧の上が切れちゃったような(?)形のカッコが曲者です。

ガウス記号?

 これはガウス記号とゆうもので、実数xに対してx以下の最大の整数とゆうことラシイ。
 ・・・チョットナニイッテルカワカンナイ;;;

 当初の理解では、ようするに実数xの整数部分だろうという。
 なでしこさんには、まさに整数部分とゆう命令がありますよ☆
 しかし、まあ、INTでよかろう。(INTは整数変換なんですが、結果は同じっぽい)
 日本語の方が読みやすいけど、長くて複雑な式を丸写しする時なんかは、記号の方が短く済んで、結局見やすかったりもする;
 で、こんな感じ?

●(日付を|日付の|日付で)修正ユリウス日取得
  日付を「/」で区切る。
  y=それ[0]。m=それ[1]。d=それ[2]。
  もし、m≦2ならば、
    y=y-1。m=m+12。
  ここまで。
  グレゴリオ暦=いいえ。
  もし、(「1582/10/15」と日付の日数差)≧0ならば、グレゴリオ暦=はい。
  もし、グレゴリオ暦=はいならば、
   (INT(365.25*y))+INT(y/400)-INT(y/100)+INT(30.59*(m-2))+d-678912で戻る。
  違えば、
   (INT(365.25*y))+INT(30.59*(m-2))+d-678914で戻る。
 ここまで。
ここまで。
#検算
「2022/1/1」の修正ユリウス日取得して表示。  #59580 合ってます!
「1582/10/15」の修正ユリウス日取得して表示。#-100840 合ってます!
「1582/10/04」の修正ユリウス日取得して表示。#-100841 合ってます!
「1/1/1」の修正ユリウス日取得して表示。     #-678577 合ってます!
「0/1/1」の修正ユリウス日取得して表示。     #-678942 あれ? →-678943
「-4712/1/1」の修正ユリウス日取得して表示。 #-2400000 あれれ?→-2400001

 紀元前になった途端、1ずつずれてる? -1しとけばいいのか? ・・・そんなコトで良いのか??

床関数!

 ここでもういちど、実数xに対してx以下の最大の整数とゆうことを落ち着いて考えると、マイナスの場合には数が大きい方が小さいってコトになるわけですから-1.5に対して-1では、xより大きいって話しになっちゃうんだね。
 1.51になるけど-1.5-2にならなければいけませんでした。

 なでしこでは、切り捨てでこの計算が出来ます。
 ふつーの感覚で言うと、切り捨てると言えば、それ以下を無かったことにして、ようするに整数部分みたいな気がするんでナゾな感じでしたが、こうゆうことなんですね。

123.45の整数部分を表示。   #123
-123.45の整数部分を表示。  #-123
123.45を整数変換して表示。 #123
-123.45を整数変換して表示。#-123
INT(123.45)を表示。       #123
INT(-123.45)を表示。      #-123

123.45を切り捨てして表示。 #123
-123.45を切り捨てして表示。#-124
FLOOR(123.45)を表示。    #123
FLOOR(-123.45)を表示。   #-124

 ちなみにマニュアルに切り捨てFLOORが同じものだという説明があるんですが、今回、床関数(フロア関数)ということを知るまで、FLOORの読みも意味も分かっていませんでした(ええっ)
 フロアって片仮名で見聞きしても何となくイメージの湧く言葉で、しかも普通に読めそうな感じの綴りなのにビックリですよね~w ワタシの頭は、アルファベットを読むということをはなから放棄しているのです;;;
 英語がムリだからプログラム辛いって、こうゆうことですよ。なんかかんか言う賢いお方は、それが分かっていらっしゃらないw

できた☆

 というわけで、INTFLOORに置換。
 あと、AD、BCともに0年というものは無いので、コンランしたくないのでBC1年は-1年ということにしたい。
 で、こんな感じ?

●(日付を|日付の|日付で)修正ユリウス日取得
  日付を「/」で区切る。
  y=それ[0]。m=それ[1]。d=それ[2]。
  もし、y=0ならば、空で戻る。
  もし、y<0ならば、y=y+1。
  もし、m≦2ならば、
    y=y-1。m=m+12。
  ここまで。
  グレゴリオ暦=いいえ。
  もし、(「1582/10/15」と日付の日数差)≧0ならば、グレゴリオ暦=はい。
  もし、グレゴリオ暦=はいならば、
   (FLOOR(365.25*y))+FLOOR(y/400)-FLOOR(y/100)+FLOOR(30.59*(m-2))+d-678912で戻る。
  違えば、
   (FLOOR(365.25*y))+FLOOR(30.59*(m-2))+d-678914で戻る。
 ここまで。
ここまで。
「2022/1/1」の修正ユリウス日取得して表示。  #59580 合ってます!
「1582/10/15」の修正ユリウス日取得して表示。#-100840 合ってます!
「1582/10/04」の修正ユリウス日取得して表示。#-100841 合ってます!
「1/1/1」の修正ユリウス日取得して表示。     #-678577 合ってます!
「-1/1/1」の修正ユリウス日取得して表示。    #-678943 合ってます!
「-4713/1/1」の修正ユリウス日取得して表示。 #-2400001 合ってます!

 できました! やったね☆

修正ユリウス日から西暦を取得

 検算はここで行います。

 計算式は、さっきの続きにあります。
 式はなんだか一段ととゆうか四段にもなっていてフクザツで目が回りますが、いっこいっこやっていけば大丈夫☆
 もう、FLOORだってバッチリ。二重になっていたって負けません!
 modは剰余、つまりは割った余りとゆうことです。
 なでしこではまさに割った余りで計算できますよ、演算子で%も使えます。(しかし、実はコレが曲者でした・・・)
 とりあえずは、こんな感じ?

#修正ユリウス日→グレゴリオ暦
●(MJDから)グレゴリオ暦取得
    n=MJD+678881。
    a=4*n+3+4*FLOOR(3/4*FLOOR((4*(n+1)/146097+1)))。
    b=5*FLOOR((a%1461)/4)+2。
    y=FLOOR(a/1461)。m=FLOOR(b/153)+3。d=FLOOR((b%153)/5)+1。
    もし、m>12ならば、
        y=y+1。m=m-12。
    ここまで。
  もし、y≦0ならば、y=y-1。
    「{y}/{m}/{d}」で戻る。
ここまで。

#修正ユリウス日→ユリウス暦
●(MJDから)ユリウス暦取得
    n=MJD+678883。
    a=4*n+3。
    b=5*FLOOR((a%1461)/4)+2。
    y=FLOOR(a/1461)。m=FLOOR(b/153)+3。d=FLOOR((b%153)/5)+1。
    もし、m>12ならば、
        y=y+1。m=m-12。
    ここまで。
  もし、y≦0ならば、y=y-1。
    「{y}/{m}/{d}」で戻る。
ここまで。

 ADについてはバッチリ合っています。

#検算
59580からユリウス暦取得して表示。   #2021/12/19
59580からグレゴリオ暦取得して表示。 #2022/1/1
0からユリウス暦取得して表示。       #1858/11/5
0からグレゴリオ暦取得して表示。     #1858/11/17
-100841からユリウス暦取得して表示。 #1582/10/4
-100841からグレゴリオ暦取得して表示。#1582/10/14
-678577からユリウス暦取得して表示。 #1/1/1
-678577からグレゴリオ暦取得して表示。#-1/12/30

 ところが、BCは・・・

-678943からユリウス暦取得して表示。   #-2/1/-28    →-1/01/01
-678943からグレゴリオ暦取得して表示。 #-2/0/0       →-2/12/30
-2400001からユリウス暦取得して表示。  #-4714/1/-28  →-4713/01/01
-2400001からグレゴリオ暦取得して表示。#-4714/-1/-5  →-4714/11/24

 いやもうね、合ってるとか合ってないとか以前のアレですよね。
 月や日に0やマイナスが発生するとか!
 なんでだ・・・orz

剰余

 ここで、問題のmodですよ!
 答えは、ここにありました。

 負数が含まれる剰余を計算した場合、言語に跨がって一意な結果が得られない・・・ですと?!
 どうゆうコトかについては、参考記事をお読み下さい。ワタシなどには説明できないヤツです。
 ともかく、剰余には複数の定義が存在していて、言語によって実装が異なると。
 プラスの時にはいいけど、マイナスになったら結果が違ってきちゃうとゆうことラシイ。
 まったく、困ったもんだ
 では、なでしこさんでやってみます。

5%3を表示。  #2
-5%3を表示。 #-2
5%-3を表示。 #2
5を3で割った余りを表示。 #2
-5を3で割った余りを表示。#-2
5を-3で割った余りを表示。#2

 どうやら絶対値最小剰余のほうに当てはまっているようですね?
 なでしこ3がというよりかはJavascriptの仕様だと思いますが、v1も同じでしたから(なでしこ同士で違っていたらさすがに困りますですね)Delphiもでしょうか。
 CやJavaなどもそうだということで、Windowsの電卓さんもこうだったので、むしろこっちが普通なんじゃと思うところですが、そんな事を言っても仕方ありません。
 おそらくこの式においては、

-5 mod 3を表示。 #1
5 mod -3を表示。 #-1

 とゆう結果が求められているものと思う。

なんとかしたい!

 まあ、そうゆうわけで混乱を招くので、先の参考サイトでは負数の剰余は使うなとの仰せな訳ですが、そんな事言ったって式がそうなってるんだからしようがない。
 整数部分に対する切り捨てみたいに、何か代わりになる命令は無いかのう・・・とマニュアルを眺めてみましたが、なさそげでした。
 が! 色々検索するうち、わりとあっさりここに書いてありました!!

なお、多くの言語では ‘%’ はリマインダー演算子ですが、言語によっては (例えば Python や Perl では) モジュロ演算子になります。正の数同士の場合は、この 2 つの値は等価ですが、被除数と除数が異なる符号の場合は結果が異なります。 JavaScript でモジュロを得るには、 a % n の代わりに ((a % n ) + n ) % n を使用してください。

 コレじゃん!
 もじゅろ? modはモジュロとやらの略だったんですかね。だから最初からmodって言ってんじゃん! ってことでしょうか。スミマセン><;
 ともかく、式もわりとカンタンですし、コレをこのまま関数にしちゃえば良さげ。
 こんな感じ?

●(AとBの)剰余
 ((A%B)+B)%Bで戻る。
ここまで。

-5と3の剰余を表示。#1
5と-3の剰余を表示。#-1

 おっとできたできた♪
 これで、なんとか出来そうじゃないですか?!

できた!!

 これなら、いっそFLOORも切り捨てで良かったと思わなくも無いけど、ともかくもこんな感じ。

#修正ユリウス日→グレゴリオ暦
●(MJDから)グレゴリオ暦取得
    n=MJD+678881。
    a=4*n+3+4*FLOOR(3/4*FLOOR((4*(n+1)/146097+1)))。
    b=5*FLOOR((aと1461の剰余)/4)+2。
    y=FLOOR(a/1461)。m=FLOOR(b/153)+3。d=FLOOR((bと153の剰余)/5)+1。
    もし、m>12ならば、
        y=y+1。m=m-12。
    ここまで。
  もし、y≦0ならば、y=y-1。
    「{y}/{m}/{d}」で戻る。
ここまで。

#修正ユリウス日→ユリウス暦
●(MJDから)ユリウス暦取得
    n=MJD+678883。
    a=4*n+3。
    b=5*FLOOR((aと1461の剰余)/4)+2。
    y=FLOOR(a/1461)。m=FLOOR(b/153)+3。d=FLOOR((bと153の剰余)/5)+1。
    もし、m>12ならば、
        y=y+1。m=m-12。
    ここまで。
  もし、y≦0ならば、y=y-1。
    「{y}/{m}/{d}」で戻る。
ここまで。

●(AとBの)剰余
 ((A%B)+B)%Bで戻る。
ここまで。
#検算
-678943からユリウス暦取得して表示。   #-1/1/1
-678943からグレゴリオ暦取得して表示。 #-2/12/30
-2400001からユリウス暦取得して表示。  #-4713/1/1
-2400001からグレゴリオ暦取得して表示。#-4714/11/24

 合いました!!!
 やったね☆

動作確認

 
 関数のコードはこっちでシンタックスハイライト付きで見れます。

 
 さらにまだなんか間違ってるとか、結果が合わないとかありましたら教えて下さい。

おわります

 ユキノはレベルアップした。
 フロアのじゅもんをおぼえた!
 ユキノはレベルアップした。
 モジュロのじゅもんをおぼえた!